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

    此篇博客所有源码均来自JDK 1.8 重入锁ReentrantLock是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少.然而读服 ...

  2. swift 有哪些学习资源

    Swift有哪些优秀的学习资源呢? 首先要推荐的当然是官方的资料了. 这个地址里放的是苹果官方为开发者提供的Swfit学习资源:https://developer.apple.com/swift/re ...

  3. 关于Vue中,使用watch同时监听两个值的实现方法

    1. 先在computed中,用需要监听的两个值(start.end)定义一个对象(dateRange) computed: { dateRange () { const { start, end } ...

  4. cookie和session以及iOS cookie的查取

    Cookie的工作原理 http是无状态的,这是什么意思呢?就是说,在没有cookie之前,你第一次访问这个页面和第二次访问这个页面, 服务器是不知道的,不知道前一次是你.那么问题来了,我怎么登录,登 ...

  5. c# 克隆来创建对象副本

  6. (2) openstack--keystone

    yun1 OpenStack packages yum install python-openstackclient -y yum install openstack-selinux SQL data ...

  7. springboot集成rabbitmq并手动注册容器实现单个queue的ack模式

    原文:https://blog.csdn.net/qq_38439885/article/details/88982373 进入正题,本文会介绍两种实现rabbitmq的ack模式的方法,分别为: 一 ...

  8. docker 运行没有权限执行文件解决

    docker运行挂载主系统目录,导致无法启动configuration error: open /etc/docker/registry/config.yml: permission denied 提 ...

  9. leetcode-2-重复的DNA序列

    所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:"ACGAATTCCG".在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助. 编写一个函 ...

  10. JSOI2009 密码 和 JSOI2007 文本生成器 和 ZOJ3545 Rescue the Rabbit

    密码 众所周知,密码在信息领域起到了不可估量的作用.对于普通的登陆口令,唯一的破解 方法就是暴力破解一逐个尝试所有可能的字母组合,但这是一项很耗时又容易被发现的工 作.所以,为了获取对方的登陆口令,在 ...