如图所示,这是一个九宫格(这倒是让我想起了小时候老师在黑板上教导我们的如何通过一系列的拼凑,将横行,竖行,以及斜行都拼到和相等),格子中有一个格子是空的,另外八个格子分别有数字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. 最近跟进一个CS项目,用到c#基础知识,准备开个分类记录一下

    C#在txt类文件中追加内容 string path = "test.txt";FileStream mystream = new FileStream(path, FileMod ...

  2. vue 鼠标移入移出事件执行多次(尤其ie)

    来自:https://www.cnblogs.com/myfirstboke/p/9150809.html  侵删 <p @mouseover="over($event)" ...

  3. springboot学习入门简易版二---springboot2.0项目创建

    2 springboot项目创建(5) 环境要求:jdk1.8+ 项目结构: 2.1创建maven工程 Group id :com.springbootdemo Artifact id: spring ...

  4. VMware15 桥接模式无法上网

    1. 检查宿主机网络连接是否成功 2. 检查宿主机网络适配器列表是否有多余的 loop 等回环类型的适配器(楼主在安装npcap程序后系统出现回环类型的适配器,即把包发回本地,所有的虚拟机的桥接模式都 ...

  5. 交付Dubbo微服务到kubernetes集群

    1.基础架构 1.1.架构图 Zookeeper是Dubbo微服务集群的注册中心 它的高可用机制和k8s的etcd集群一致 java编写,需要jdk环境 1.2.节点规划 主机名 角色 ip hdss ...

  6. 25道Shell面试题

    1. 用sed修改test.txt的23行test为tset: sed –i ‘23s/test/tset/g’ test.txt 2. 查看/web.log第25行第三列的内容. sed –n ‘2 ...

  7. 【HDU-1045,Fire Net-纯暴力简单DFS】

    原题链接:点击!   大致题意:白块表示可以放置炮台的位置——每个炮台可以攻击到上下左右的直线上的炮台(也就是说在它的上下左右直线上不可以再放置炮台,避免引起互相攻击),黑块表示隔离墙的位置——不可放 ...

  8. P1351 联合权值[鬼畜解法]

    题目描述 无向连通图 G 有 n 个点,n−1 条边.点从 1 到 n 依次编号,编号为 i 的点的权值为 Wi​,每条边的长度均为 1.图上两点 (u,v) 的距离定义为 u 点到 v 点的最短距离 ...

  9. 9 loader - 分析webpack调用第三方loader的过程

    注意:webpack处理第三方文件类型的过程: 1.发现这个要处理的文件不是JS文件,然后就去配置文件中,查找有没有对应的第三方loader规则 2.如果能找到对应的规则,就会调用对应的loader处 ...

  10. Linux环境下安装mysql5.6(二进制包不是rpm格式)

    一.准备: 1.CentOS release 6.8 2.mysql-5.6.31-linux-glibc2.5-x86_64.tar.gz 3.Linux下MySQL5.6与MySQL5.7安装方法 ...