如图所示,这是一个九宫格(这倒是让我想起了小时候老师在黑板上教导我们的如何通过一系列的拼凑,将横行,竖行,以及斜行都拼到和相等),格子中有一个格子是空的,另外八个格子分别有数字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. 使用docker安装gitlab

    我这里使用的系统是centos7 首先安装docker,docker-compose(非必须,但是使用它可以简化镜像启动参数),需要注意的是docker-compose安装依赖Python的pip,所 ...

  2. body onload()事件和table insertRow()、tr insertCell()

    onload事件: 定义和用法: onload 事件会在页面或图像加载完成后立即发生. onload 通常用于 <body> 元素,在页面完全载入后(包括图片.css文件等等.)执行脚本代 ...

  3. 张量(tensor)的广播

    在使用numpy 对张量(数组)进行操作时,两个形状相同的张量进行加减等运算很容易理解,那么不同形状的张量之间的运算是通过广播来实现的.广播实际上很简单,但是弄清楚是也花了不小功夫,这里记录一下. 广 ...

  4. dnmp安装

    centos7.2.box下载地址 链接: https://pan.baidu.com/s/1ny20PN2x7YuA6dwYA-P0yQ 提取码: wrdk 1 下载centos.box 新建dnm ...

  5. Django+bootstrap+注册登录系统

    转自:https://www.cnblogs.com/robindong/p/9610057.html Robin_D 博客园 首页 新随笔 联系 订阅 管理 随笔 - 10  文章 - 0  评论 ...

  6. git使用——忽略文件

    一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表. 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等.git下有2种方式实现忽略文件的目的.方法一 ...

  7. JAVA 使用原生jdbc批量添加,表被锁住问题

    今天用jdbc批量添加数据的时候遇到了一个问题,当数据添加成功过后,再想对该表进行操作发现表被锁住了,检查了下代码发现事务提交了呀!!!!!!!!!!!! 去网上查了大半天的资料才发现问题,在conn ...

  8. 浅谈Java中的AOP面向切面的变成和控制反转IOC

    https://blog.csdn.net/hi_kevin/article/details/7325554 https://www.cnblogs.com/zedosu/p/6632260.html ...

  9. Stages — 研发过程可视化建模和管理平台

    Stages 是德国Method park公司的产品,用于帮助企业定义.管理.发布.控制.优化其研发过程,同时使其研发过程符合CMMI.ASPICE.ISO26262等标准.Stages的核心理念是把 ...

  10. G1垃圾收集器原理剖析【官方解读】

    继续基于上一次https://www.cnblogs.com/webor2006/p/11135005.html的官方G1文档进行解读,上一次分析到了这: 话不多说,继续往前读: When perfo ...