Eight(经典题,八数码)

Posted happy_code

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Eight(经典题,八数码)相关的知识,希望对你有一定的参考价值。

Eight

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 20993    Accepted Submission(s): 5634
Special Judge

Problem Description

The 15-puzzle has been around for over 100 years; even if you don\'t know it by that name, you\'ve seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let\'s call the missing tile \'x\'; the object of the puzzle is to arrange the tiles so that they are ordered as: 


1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 x


where the only legal operation is to exchange \'x\' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle: 


1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4
5 6 7 8 5 6 7 8 5 6 7 8 5 6 7 8
9 x 10 12 9 10 x 12 9 10 11 12 9 10 11 12
13 14 11 15 13 14 11 15 13 14 x 15 13 14 15 x
r-> d-> r->


The letters in the previous row indicate which neighbor of the \'x\' tile is swapped with the \'x\' tile at each step; legal values are \'r\',\'l\',\'u\' and \'d\', for right, left, up, and down, respectively. 

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and 
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing \'x\' tile, of course). 

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three 
arrangement.

 

 

Input

You will receive, several descriptions of configuration of the 8 puzzle. One description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus \'x\'. For example, this puzzle 

1 2 3 
x 4 6 
7 5 8 

is described by this list: 

1 2 3 x 4 6 7 5 8

 

 

Output

You will print to standard output either the word ``unsolvable\'\', if the puzzle has no solution, or a string consisting entirely of the letters \'r\', \'l\', \'u\' and \'d\' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line. Do not print a blank line between cases.

 

 

Sample Input

2 3 4 1 5 x 7 6 8

 

 

Sample Output

ullddrurdllurdruldr

 

 

//就是类似九宫格那个游戏,不过这里9个格子大小都相等

//学了比较多的东西,才懂怎么做

这里我用的的是 A*+逆序数剪枝+hash判重 做的

A*其实也好理解,就是 bfs 升级版

这个博客写的很详细  http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx

因为,无论怎么移动,逆序数的奇偶性是不变的,所以用这个能剪枝

最大的问题就是怎么判重了,最多不过 9!种情况么,362880 ,每种情况对应一个数字,用一个 vis[ ]就能判重了,然后hash 判重就好理解了

还有许多方法,推荐一篇博客 http://www.cnblogs.com/goodness/archive/2010/05/04/1727141.html 有兴趣可以看看

 

A* + hash 判重 + 曼哈顿距离

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <cstdlib>
  5 #include <algorithm>
  6 #include <iostream>
  7 #include <queue>
  8 #include <map>
  9 #include <vector>
 10 using namespace std;
 11 
 12 const int maxn=4e5+10;// 400010 最多不超过这么多状态 362880
 13 const int hash_[9]={1,1,2,6,24,120,720,5040,40320};
 14 struct Node
 15 {
 16     int state[3][3];
 17     int x,y;
 18     int g,h;        //g代表已耗费,h代表估计耗费
 19     int hash_num;   //这个状态的 hash 值
 20     bool operator < (const Node PP) const
 21     {
 22         //return g+h > PP.g+PP.h;
 23         //1638ms
 24         return h==PP.h ? g>PP.g : h>PP.h;
 25         //686ms
 26     }
 27 }star,ans;
 28 
 29 int dx[4]={1,-1,0,0};
 30 int dy[4]={0,0,1,-1};
 31 char op_c[8]={"durl"};
 32 struct Node2
 33 {
 34     int pre_hash;
 35     char op;
 36 }p[maxn];
 37 int vis[maxn];
 38 
 39 
 40 int count_hash(Node p)//获得hash值,计算的是 0-8 排列的hash
 41 {
 42     int i,j,k,hash_num=0;
 43     for (i=0;i<9;i++)
 44     {
 45         k=0;
 46         for (j=0;j<i;j++)
 47         {
 48             if (p.state[j/3][j%3]>p.state[i/3][i%3])
 49                 k++;
 50         }
 51         hash_num+=k*hash_[i];
 52     }
 53     return hash_num;
 54 }
 55 
 56 int count_h(Node p)//注意位置,要细致
 57 {
 58     int i,all=0;
 59     for (i=0;i<9;i++)
 60     {
 61         int e=p.state[i/3][i%3];
 62         if (e)
 63         {
 64             e-=1;
 65             all+=abs(i/3-e/3)+abs(i%3-e%3);
 66         }
 67     }
 68     return all;
 69 }
 70 
 71 
 72 void print(int h)
 73 {
 74     if (p[h].pre_hash==-1) return;
 75     print(p[h].pre_hash);
 76     printf("%c",p[h].op);
 77 }
 78 
 79 void A_star()
 80 {
 81     int i;
 82 
 83     star.hash_num = count_hash(star);
 84     star.g=0;
 85     star.h=count_h(star);
 86 
 87     memset(vis,0,sizeof(vis));
 88     vis[star.hash_num]=1;
 89     p[star.hash_num].pre_hash=-1; //头节点
 90 
 91     priority_queue <Node> Q;
 92     Q.push(star);
 93 
 94     Node e,n;
 95     int xx,yy;
 96 
 97     if (star.hash_num==ans.hash_num)//这个不能丢
 98     {
 99         printf("\\n");
100         return;
101     }
102     while (!Q.empty())
103     {
104         e=Q.top();
105         Q.pop();
106 
107         for (i=0;i<4;i++)
108         {
109             xx=e.x+dx[i];
110             yy=e.y+dy[i];
111             if (xx<0||yy<0||xx>=3||yy>=3) continue;
112 
113             n=e;
114             n.x=xx;
115             n.y=yy;
116             swap(n.state[xx][yy],n.state[e.x][e.y]);
117             n.g++;
118             n.h=count_h(n);
119             n.hash_num=count_hash(n);
120 
121             if (vis[n.hash_num]) continue;
122 
123             p[n.hash_num].pre_hash=e.hash_num;  //记录这个状态的的父 hash
124             p[n.hash_num].op=op_c[i];           //记录怎么由父hash移动来的
125 
126             vis[n.hash_num]=1;
127             if (n.hash_num==ans.hash_num)//说明到了
128             {
129                 print(n.hash_num);
130                 printf("\\n");
131                 return;
132             }
133             Q.push(n);
134         }
135     }
136 }
137 
138 int main()
139 {
140     char str[30];
141     int i;
142 
143     for(i=0;i<9;i++)                    //终点
144         ans.state[i/3][i%3]=(i+1)%9;
145     ans.hash_num=count_hash(ans);       //终点
146 
147     while(gets(str))
148     {
149         int i;
150         int len=strlen(str);
151 
152         int j=0;
153         for(i=0,j=0;i<len;i++)
154         {
155             if(str[i]==\' \')continue;
156             if(str[i]==\'x\')
157             {
158                 star.state[j/3][j%3]=0;
159                 star.x=j/3;
160                 star.y=j%3;
161             }
162             else star.state[j/3][j%3]=str[i]-\'0\';
163             j++;                         // j/3 记录行数
164         }
165 
166         //判断逆序数
167         int temp [9],k=0;
168         for (i=0;i<9;i++)
169             temp[i]=star.state[i/3][i%3];
170         for (i=0;i<9;i++)
171         {
172             if (temp[i]==0) continue;
173             for (int j=0;j<i;j++)
174                 if (temp[j]>temp[i]) k++;
175         }
176         if (k%2)
177             printf("unsolvable\\n");
178         else
179             A_star();
180     }
181     return 0;
182 }
183 
184 /*//错在这,浪费我2小时,才找出来! == 竟然不报错,而且是全局变量,第一次也不会错!!!
185 
186         for (i=0,j=0;i<len;i++)
187         {
188             if (str[i]==\' \')continue;
189             else if (str[i]==\'x\')
190             {
191                 star.x=j/3;
192                 star.y=j%3;
193                 star.state[j/3][j%3]==0;
194             }
195             else star.state[j/3][j%3]=str[i]-\'0\';
196             j++;
197         }
198 */
View Code

 

IDA* + 曼哈顿距离 这个空间就省下来了,改一下,十五数码也能做的,上一个就不行了,内存爆炸

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <math.h>
  4 #include <stdlib.h>
  5 
  6 #define size 3          //这里规定几数码
  7 
  8 int move[4][2]={{-1,0},{0,-1},{0,1},{1,0}};//上 左 右 下  置换顺序
  9 char op[4]={\'u\',\'l\',\'r\',\'d\'};
 10 
 11 int map[size][size],map2[size*size],limit,path[100];
 12 int flag,length;
 13 
 14 // 十五数码 的表
 15 //int goal[16][2]= {{size-1,size-1},{0,0},{0,1},{0,2},
 16 //                  {0,3},{1,0},{1,1},{1,2},
 17 //                  {1,3},{2,0},{2,1},{2,2},
 18 //                  {2,3},{3,0},{3,1},{3,2}};//各个数字应在位置对照表
 19 
 20 // 八数码 的表
 21 int goal[9][2]= {{size-1,size-1},{0,0},{0,1},{0,2},
 22                                  {1,0},{1,1},{1,2},
 23                                  {2,0},{2,1}};      //各个数字应在位置对照表
 24 
 25 int nixu(int a[size*size])
 26 {
 27     int i,j,ni,w,x,y;  //w代表0的位置 下标,x y 代表0的数组坐标
 28     ni=0;
 29     for(i=0;i<size*size;i++)  //,size*size=16
 30     {
 31         if(a[i]==0)  //找到0的位置
 32             continue;//w=i;
 33 
 34         for(j=i+1;j<size*size;j++)  //注意!!每一个都跟其后所有的比一圈 查找小于i的个数相加
 35         {
 36             if (a[j]==0)continue;
 37             if(a[i]>a[j])
 38                 ni++;
 39         }
 40     }
 41     //x=w/size;
 42     //y=w%size;
 43     //ni+=abs(x-(size-1))+abs(y-(size-1));  //最后加上0的偏移量
 44     if(ni%2==0)
 45         return 1;
 46     else
 47         return 0;
 48 }
 49 
 50 int hv(int a[][size])//估价函数,曼哈顿距离,小等于实际总步数
 51 {
 52     int i,j,cost=0;
 53     for(i=0;i<size;i++)
 54     {
 55         for(j=0;j<size;j++)
 56         {
 57             int w=map[i][j];
 58             cost+=abs(i-goal[w][0])+abs(j-goal[w][1]);
 59         }
 60     }
 61     return cost;
 62 }
 63 
 64 void swap(int*a,int*b)
 65 {
 66     int tmp;
 67     tmp=*a;
 68     *a=*b;
 69     *b=tmp;
 70 }
 71 
 72 void dfs(int sx,int sy,int len,int pre_move)//sx,sy是空格的位置
 73 {
 74     int i,nx,ny;
 75 
 76     if(flag)
 77         return;
 78 
 79     int dv=hv(map);
 80 
 81     if (len==limit)
 82     {
 83         if(dv==0)  //成功! 退出
 84         {
 85             flag=1;
 86             length=len;
 87             return;
 88         }
 89         else
 90             return;  //超过预设长度 回退
 91     }
 92     else if(len<limit)
 93     {
 94         if(dv==0)  //短于预设长度 成功!退出
 95         {
 96             flag=1;
 97             length=len;
 98             return;
 99         }
100     }
101 
102     for(i=0;i<4;i++)
103     {
104         if(i+pre_move== 3&& len>0)//不和上一次移动方向相反,对第二步以后而言
105             continue;
106         nx=sx+move[i][0];  //移动的四步 上左右下
107         ny=sy+move[i][1];
108 
109         if( 0<=nx && nx<size && 0<=ny && ny<size )  //判断移动合理
110         {
111             swap(&map[sx][sy],&map[nx][ny]);
112             int p=hv(map);   //移动后的 曼哈顿距离p=16
113 
114             if(p+len<=limit&&!flag)  //p+len<=limit&&!flag剪枝判断语句
115             {
116                 path[len]=i;
117                 dfs(nx,ny,len+1,i);  //如当前步成功则 递归调用dfs
118                 if(flag)
119                     return;
120             }
121             swap(&map[sx][sy],&map[nx][ny]);  //不合理则回退一步
122         }
123     }
124 }
125 
126 int main()
127 {
128     int pp;
129     int i,j,k,sx,sy;
130     char str[30];
131 
132     while(gets(str))
133     {
134         int slen = strlen(str);
135         j=0;
136         for(i=0;i<slen;i++)
137         {
138             if (str[i]==\' \')
139                 continue;
140             if (str[i]==\'x\')
141                 map2[j]=0;
142             else
143                 map2[j]=str[i]-\'0\';
144             j++;
145         }
146 
147         for(i=0;i<size*size;i++)  //给map 和map2赋值map是二维数组,map2是一维数组
HDU 1043 Eight八数码解题思路(bfs+hash 打表 IDA* 等)

poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)

A – EIGHT

Hdu 1043 Eight (八数码问题)

八数码问题 Eight Digital Problem

广度优先搜索-八数码问题