如图所示,这是一个九宫格(这倒是让我想起了小时候老师在黑板上教导我们的如何通过一系列的拼凑,将横行,竖行,以及斜行都拼到和相等),格子中有一个格子是空的,另外八个格子分别有数字1--8,我们的任务是将原图通过空格转换为前面八格为1--8,而最后一格为空。

以上的截图来自如下的一款android游戏(当然,由于版本的原因,样式换成了一种木板式的,更贴近于我们在现实中的“八数码游戏”),其名字叫:8--Puzzle,在其软件的启动界面中,有阐述两种游戏的模式:

何者为难?何者为易呢?这里我们有一个可以定量化的衡量标准,也就是说,我们可以以该状态还原为目标状态(这里称为Goal-Status)所需的最小步数为一个凭据。当然,这样比较单纯,我们还可以设计出更“客观”一些的,比如,有些移动是难以想到的,而有些是很容易想到的,为每一个步数赋予一个权值,这就是一个不错的想法。

那么,关键的问题是,我们需要设计出一个AI出来,只要给出初始的状态,我们可以通过我们的AI来得到到目标状态具体需要多少步,甚至,我们可以得到每一步的具体操作过程(这里可以用u,d,l,r分别来代表上下左右)

AI的实现方法很多,比如:裸体的BFS(Bruce Force+BFS),双向BFS+STL,开哈希表之后,更省时间,更牛的一些办法还有楼教主在2005年的百度之星总决赛打出的A*(该算法基于启发式,后来,百度公司在之后的比赛中就以Astar作为百度之星的象征,其来源就是楼天成的A*算法)以及其进一步的IDA*算法。在博客园中,有牛人归纳出了八数码问题的八重境界,我会在最后进行转载的。

有人说过,没有看过该AI的人,人生不完美,也许,该问题真的比较深刻吧!

我们利用X来表示这个8--PUZZLE问题的空格,当然,在Input中,可以将其归为一行(毕竟,大家都知道这是个八数码问题吧!)

Input : 1 2 3 x 4 6 7 5 8

对于输出来说,我们只要输出一个方向就可以了(根据方向,我们可以知道是哪一个方块在动),对于这个Input,我们所得到的Output应该是:lul

Output:lul

而对于最少的次数,我们可以根据输出中的字母的数量得到。

首先,考虑一个问题,我们的状态总数(算上那个空格)一共为9! = 362880 种,于是,即使是最野蛮的暴力+BFS也是可以的(纯粹的暴力的话,果断还是不行的),这里给出的一种方法是暴力+BFS+queue容器+Hash表。

Solve:

  1 #include <iostream>

  2 

  3  #include <cstdio>

  4 

  5  #include <cstring>

  6 

  7  #include <string>

  8 

  9  #include <algorithm>

 10 

 11  #include <cmath>

 12 

 13  #include <queue>

 14 

 15  

 16 

 17  using namespace std;

 18 

 19  

 20 

 21  #define MAXN 363000  //9!==326880

 22 

 23  

 24 

 25  struct node

 26 

 27  {

 28 

];//当前状态

 30 

 31    int loc;//'0'的位置,即'x'的位置

 32 

 33    int stat;//康托展开的hash值

 34 

 35    string path;//路径

 36 

 37  };

 38 

 39  

 40 

 41  //分别存储1--9的阶乘值

 42 

,,,,,,,,,};

 44 

 45  //同以前一样,用一个二维数组dir[4][2]来装填方向

 46 

][]={-,,,,,-,,};

 48 

 49  //存储方向字符,以便在之后表明路径

 50 

]="udlr";

 52 

;//123456780对应的hash值

 54 

 55  string path;//路径

 56 

 57  //作为一种标记,遍历已经访问过的状态

 58 

 59  bool vis[MAXN];

 60 

 61  //最初的结点

 62 

 63  node st;

 64 

 65  

 66 

 67  //康托展开求序列的hash值

 68 

 69  int cantor(const int *s)

 70 

 71  {

 72 

;

 74 

;i<;i++)

 76 

 77    {

 78 

;

 80 

;j<i;j++)

 82 

 83        if(s[j]>s[i])

 84 

 85          num++;

 86 

 87      sum+=(num*fac[i]);

 88 

 89    }

 90 

);

 92 

 93  }

 94 

 95  

 96 

 97  //这里利用越界函数来标识是否越界

 98 

 99  bool isok(int x,int y)

 

  {

 

     || x> || y> || y<)

 

      return false;

 

    return true;

 

  }

 

  

 

  bool bfs()

 

  {

 

    //初始化vis数组,将其都标记为0

 

    memset(vis,,sizeof(vis));

 

    //队列容器来装载结点

 

    queue<node>q;

 

    node cur,tmp;

 

    //初始结点进入队列

 

    q.push(st);

 

    vis[st.stat]=;

 

    //如果队列非空,那么就一直重复这个过程

 

    while(!q.empty())

 

    {

 

      //将首结点取出作为当前结点

 

      cur=q.front();

 

      //将队列剥离一个

 

      q.pop();

 

      //得到空白方块的横坐标和纵坐标

 

      ,y=cur.loc%;

 

      ;i<;i++)

 

      {

 

        //从四个方向对空白位置进行搜索

 

        ],ty=y+dir[i][];

 

        //如果越界的话,换一个方向

 

        if(!isok(tx,ty))

 

          continue;

 

        tmp=cur;

 

        tmp.loc=tx*+ty;//'0'移动到该位置

 

        //这里,相当于交换了一个方向

 

        tmp.s[cur.loc]=tmp.s[tmp.loc];

 

        tmp.s[tmp.loc]=;

 

        //获取这个状态的hash值

 

        tmp.stat=cantor(tmp.s);

 

        //如果这个状态没有被遍历过的话

 

        if(!vis[tmp.stat])

 

        {

 

          vis[tmp.stat]=;

 

          //修改路径,字符串可以直接在后面加

 

          tmp.path=cur.path+index[i];

 

          //如果是正解的话

 

          if(tmp.stat==aim)

 

          {

 

            path=tmp.path;

 

            return true;

 

          }

 

          //如果不是最优解的话,继续入队列

 

          q.push(tmp);

 

        }

 

      }

 

    }

 

    ;

 

  }

 

  

 

  int main()

 

  {

 

    //假设操作的总次数不大于256

 

    ];

 

    //利用gets()函数读入一行

 

    while(gets(buf))

 

    {

 

      int len=strlen(buf);

 

      ;

 

      ;i<len;i++)

 

      {

 

        ')

 

        {

 

          st.s[cnt++]=buf[i]-';

 

        }

 

        else if(buf[i]=='x')

 

        {

 

          st.s[cnt]=;

 

          //这里标识空白区域的具体位置

 

          st.loc=cnt++;

 

        }

 

      }

 

      //利用cantor扩展函数得到初始状态

 

      st.stat=cantor(st.s);

 

      //如果一开始就是目标状态的话,那么就输出空气好咯!

 

      if(st.stat==aim)

 

      {

 

        puts("");

 

        continue;

 

      }

 

      //广搜开始

 

      if(bfs())

 

        //输出路径

 

        cout<<path<<endl;

 

      else

 

        //输出不可解

 

        puts("unsolvable");

 

    }

 

    ;

 

  }

拼图游戏(8 puzzle)的更多相关文章

  1. [CareerCup] 8.6 Jigsaw Puzzle 拼图游戏

    8.6 Implement a jigsaw puzzle. Design the data structures and explain an algorithm to solve the puzz ...

  2. 利用Vue.js实现拼图游戏

    之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5 ...

  3. JavaScript拼图游戏

    今天是2016年最后一天上班了.最近几天都比较休闲,有时间空闲下来写写文档之类的. 2016过得真是快.感觉没做什么就过去了.想到之前想坚持每个月写一写博客都没坚持到.希望2017年可以吧. 无聊之余 ...

  4. 用Qt图形视图框架开发拼图游戏

    用Qt的图形视图框架(Graphics View Framework)做了一个拼图游戏DEMO,演示了: QGraphicsView.QGraphicsScene.QGraphicsItem的基本用法 ...

  5. Vue.js实现拼图游戏

    Vue.js实现拼图游戏 之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/ ...

  6. 程序设计 之 C#实现《拼图游戏》

    功能描述: 1.用户自定义上传图片 2.游戏难度选择:简单(3*3).一般(5*5).困难(9*9)三个级别 3.纪录完成步数 模块: 1.拼图类 2.配置类 3.游戏菜单窗口 4.游戏运行窗口 -- ...

  7. 程序设计 之 C#实现《拼图游戏》 (下) 原理篇

    前言:在 http://www.cnblogs.com/labixiaohei/p/6698887.html 程序设计 之 C#实现<拼图游戏>(上),上传了各模块代码,而在本文中将详细剖 ...

  8. 拼图游戏js

    实现算法: 1. JavaScript动态生成拼图:通过生成16个div,且除最后一个div不使用背景图片以外,其他div都设置拼图图片为背景.然后通过调整background-position来实现 ...

  9. 程序设计 之 C#实现《拼图游戏》 (上)代码篇

    原理详解请参考博客中 拼图游戏(下)原理篇 http://www.cnblogs.com/labixiaohei/p/6713761.html 功能描述: 1.用户自定义上传图片 2.游戏难度选择:简 ...

  10. html+css+js实现网页拼图游戏

    代码地址如下:http://www.demodashi.com/demo/14449.html 项目描述 使用 html+js+css 实现一个网页拼图游戏,可支持简单,中等,困难三种难度. 演示效果 ...

随机推荐

  1. 使用 pykafka 进行消费

    kafka连接脚本 环境:python3,用到的模块有 pykafka,kazoo # coding=utf-8 import pykafka class KafkaReaderThread(obje ...

  2. 图片预先加载 preloadjs

    <body><div class="loading"> <div class="progress"></div> ...

  3. element-ui default-checked-keys 会把节点下所有子节点全部勾选解决方法

    <el-tree class="filter-tree" :data="permissionData" :props="props" ...

  4. jupyter修改默认目录

    有趣的事,Python永远不会缺席! 如需转发,请注明出处:小婷儿的python https://www.cnblogs.com/xxtalhr/p/10841241.html 一.修改 win10 ...

  5. 高性能的js第三方库——lodash、 Underscore、async、md5及moment

    背景:为了实现某些功能,如:数据排序.分组.筛选.深拷贝等,自己写的函数或网上搜索处理的转换函数质量无法保证,这时直接使用成熟的js第三方库是首选. *注:“framework(框架)”,“libra ...

  6. NLP使用pytorch框架,pytorch安装

    pytorch的安装方法及出现问题的解决方案: 安装pytorch,使用pip 安装,在运行代码的时候会报错,但是导包的时候不会报错,因此要采用conda的方式安装   1.找到miniconda的网 ...

  7. Yum三方仓库——RPMForge

    参考:How to Enable RPMForge Repository in RHEL/CentOS 7.x/6.x/5.x RPMForge / RepoForge这两个项目已经死亡,不应该使用 ...

  8. 用js刷剑指offer(二叉树中和为某一值的路径)

    题目描述 输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径.路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.(注意: 在返回值的list中,数组长度大 ...

  9. Java程序员完美设置,Mac编程指南

        重装了不知道多少次Windows,Linux发行版换来换去总是觉得不满意,终于下定决心在年头买了人生中第一台Mac. 为什么是Mac 现在的移动端.服务器端跑的大多数都是Unix系统,熟悉Un ...

  10. zoj 4099 Extended Twin Composite Number

    Do you know the twin prime conjecture? Two primes  and  are called twin primes if . The twin prime c ...