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
Sample Output
//就是类似九宫格那个游戏,不过这里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 */
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* 等)