【BFS / DBFS / A* / IDA*】——POJ1077——八数码问题
Eight
| Time Limit: 1000MS | Memory Limit: 65536K | |||
| Total Submissions: 29472 | Accepted: 12811 | Special Judge | ||
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:
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:
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.
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 a description of a configuration of the 8 puzzle. The 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
is described by this list:
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.
Sample Input
2 3 4 1 5 x 7 6 8
Sample Output
ullddrurdllurdruldr
题意:解决八数码问题并打印路径
思路:
一、BFS+康托展开判重
康托展开:(康托展开就是一种特殊的哈希函数)
把一个整数X展开成如下形式:
其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]<i(1<=i<=n)
补充:康托展开的逆运算可求全排列(见课件)
枚举路径并记入结构体
代码如下:704ms
#include<stdio.h> #include<queue> #include<string.h> #include<iostream> #include<algorithm> using namespace std; const int MAXN=1000000; int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//康拖展开判重 // 0!1!2!3!4! 5! 6! 7! 8! 9! bool vis[MAXN];//标记 int cantor(int s[])//康拖展开求该序列的hash值 { int sum=0; for(int i=0;i<9;i++) { int num=0; for(int j=i+1;j<9;j++) if(s[j]<s[i])num++; sum+=(num*fac[9-i-1]); } return sum+1; } struct Node { int s[9]; int loc;//“0”的位置,把“x"当0 int status;//康拖展开的hash值 string path;//路径 }ncur; string path; int aim=46234;//123456780对应的康拖展开的hash值 int move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//u,d,l,r char indexs[5]="udlr";//正向搜索 bool bfs() { memset(vis,false,sizeof(vis)); Node cur,next; queue<Node>q; q.push(ncur); while(!q.empty()) { cur=q.front(); q.pop(); if(cur.status==aim) { path=cur.path; return true; } int x=cur.loc/3; int y=cur.loc%3; for(int i=0;i<4;i++) { int tx=x+move[i][0]; int ty=y+move[i][1]; if(tx<0||tx>2||ty<0||ty>2)continue; next=cur; next.loc=tx*3+ty; next.s[cur.loc]=next.s[next.loc];//换位 next.s[next.loc]=0;//换位 next.status=cantor(next.s); if(!vis[next.status])//记录路径 { vis[next.status]=true; next.path=next.path+indexs[i]; if(next.status==aim) { path=next.path; return true; } q.push(next); } } } return false; } int main() { char ch; while(cin>>ch) { if(ch=='x') { ncur.s[0]=0; ncur.loc=0; } else ncur.s[0]=ch-'0'; for(int i=1;i<9;i++) { cin>>ch; if(ch=='x') { ncur.s[i]=0; ncur.loc=i; } else ncur.s[i]=ch-'0'; } ncur.status=cantor(ncur.s); if(bfs()) { cout<<path<<endl; } else cout<<"unsolvable"<<endl; } return 0; }
二、DBFS+康托展开+回溯保存路径
代码如下:0ms
#include<stdio.h> #include<string.h> #include<iostream> #include<queue> using namespace std; const int MAXN = 400000; int has[MAXN]; int fac[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320}; int dx[] = {-1, 0, 1, 0}; int dy[] = {0, -1, 0, 1}; int last1[MAXN], last2[MAXN]; int mov1[MAXN], mov2[MAXN]; struct State { int s[9]; int pos; int hash; } ini, target;//target是目标状态 queue<State> q1, q2; int Hash(int* s)//康托展开 { int res = 0; for(int i = 0; i < 8; i++) { int cnt = 0; for(int j = i + 1; j < 9; j++) { if(s[i] > s[j]) cnt++; } res += cnt * fac[8-i]; } return res; } int cal_pos(int pos, int i)//计算0的位置 { int nx = pos/3+dx[i]; int ny = pos%3+dy[i]; if(nx<0 || nx>2 || ny<0 || ny>2) return -1; return nx*3 + ny; } void DBFS_init() { while(!q1.empty()) q1.pop(); while(!q2.empty()) q2.pop(); for(int i = 0; i < 9; i++) target.s[i] = i+1; target.pos = 8; target.hash = 0; has[0] = 2; q2.push(target); q1.push(ini); has[ini.hash] = 1; } int q1_expand() { if(q1.empty()) return -1; State ha = q1.front(); q1.pop(); State tmp; for(int i = 0; i < 4; i++) { tmp.pos = cal_pos(ha.pos, i); if(tmp.pos<0) continue; for(int j = 0; j < 9; j++) { if(j==ha.pos) tmp.s[j] = ha.s[tmp.pos]; else if(j == tmp.pos) tmp.s[j] = ha.s[ha.pos]; else tmp.s[j] = ha.s[j]; } tmp.hash = Hash(tmp.s); if(has[tmp.hash] == 1) continue; q1.push(tmp); last1[tmp.hash] = ha.hash; mov1[tmp.hash] = i; if(has[tmp.hash] == 2) return tmp.hash; has[tmp.hash] = 1; } return -1; } int q2_expand() { if(q2.empty()) return -1; State ha = q2.front(); q2.pop(); State tmp; for(int i = 0; i < 4; i++) { tmp.pos = cal_pos(ha.pos, i); if(tmp.pos<0) continue; for(int j = 0; j < 9; j++) { if(j==ha.pos) tmp.s[j] = ha.s[tmp.pos]; else if(j == tmp.pos) tmp.s[j] = ha.s[ha.pos]; else tmp.s[j] = ha.s[j]; } tmp.hash = Hash(tmp.s); if(has[tmp.hash] == 2) continue; q2.push(tmp); last2[tmp.hash] = ha.hash; mov2[tmp.hash] = i; if(has[tmp.hash] == 1) return tmp.hash; has[tmp.hash] = 2; } return -1; } int DBFS() { DBFS_init(); while(!q1.empty() || !q2.empty()) { int ans1 = q1_expand(); if(ans1 >= 0) return ans1; int ans2 = q2_expand(); if(ans2 >= 0) return ans2; } return -1; } void print_path1(int x) { if(x==ini.hash) return; print_path1(last1[x]); int i = mov1[x]; if(!i) printf("u"); else if(i==1) printf("l"); else if(i==2) printf("d"); else printf("r"); } void print_path2(int x) { if(x==0) return; int i = mov2[x]; if(!i) printf("d"); else if(i==1) printf("r"); else if(i==2) printf("u"); else printf("l"); print_path2(last2[x]); } int main() { char tmp; while(cin >> tmp) { memset(has, 0, sizeof(has)); if(tmp != 'x') ini.s[0] = tmp - '0'; else { ini.s[0] = 9; ini.pos = 0; } for(int i = 1; i < 9; i++) { cin >> tmp; if(tmp == 'x') { ini.s[i] = 9; ini.pos = i; } else ini.s[i] = tmp - '0'; } ini.hash = Hash(ini.s); int ans = DBFS(); if(ans < 0) printf("unsolvable"); else { print_path1(ans); print_path2(ans); } puts(""); } return 0; }
三、A*+曼哈顿距离
利用优先队列,重载结构体运算符
代码如下:16ms
#include <iostream> #include <queue> #include <string.h> #include<cmath> #include<stdio.h> using namespace std; const int maxN=500000; const int aim=46233; int fac[]={1,1,2,6,24,120,720,5040,40320}; int vis[maxN],g[maxN]; int dirx[]={0,0,-1,1}; int diry[]={-1,1,0,0}; int goal_x[]={0,0,0,1,1,1,2,2}; int goal_y[]={0,1,2,0,1,2,0,1}; char word[8]="lrud"; struct NODE { int s[9]; int loc; //location of '0' int status;//cantor value int f;//f=g+h string path; bool operator <(const NODE &a) const{ return f>a.f; } }; priority_queue<NODE> open; string path; int cantor(int s[]) { int sum=0; for(int i=0;i<9;++i) { int cnt=0; for(int j=i+1;j<9;++j) if(s[i]>s[j]) ++cnt; sum+=cnt*fac[9-i-1]; } return sum; } //该点与目标函位置的哈密顿距离 int h(int s[]) { int sum=0; for(int i=0;i<3;++i) for(int j=0;j<3;++j) { int k=i*3+j; if(s[k]!=9) sum+=fabs(i-goal_x[s[k]-1])+fabs(j-goal_y[s[k]-1]); } return sum; } int a_star(NODE start) { memset(vis,0,sizeof(vis)); while(!open.empty()) open.pop(); NODE cur,nxt; open.push(start); vis[start.status]=1; while(!open.empty()) { cur=open.top(); open.pop(); if(cur.status==aim) { path=cur.path; return 1; } int x=cur.loc/3; int y=cur.loc%3; for(int i=0;i<4;++i) { int tx=x+dirx[i]; int ty=y+diry[i]; if(tx<0||tx>=3||ty<0||ty>=3) continue; nxt=cur; nxt.loc=tx*3+ty; nxt.s[cur.loc]=nxt.s[nxt.loc]; nxt.s[nxt.loc]=0; nxt.status=cantor(nxt.s); if(vis[nxt.status]==0)//0:not visited 1:in colse or open { vis[nxt.status]=1; g[nxt.status]=g[cur.status]+1; nxt.f=g[nxt.status]+3*h(nxt.s);//h>h* can not always found the best solution,but can find a solution nxt.path=nxt.path+word[i]; open.push(nxt); } } vis[cur.status]=1; } return 0; } int main() { char tmp[4]; NODE ncur; for(int i=0;i<9;++i) { scanf("%s",tmp); if(tmp[0]=='x') { ncur.s[i]=0; ncur.loc=i; } else ncur.s[i]=tmp[0]-'0'; } ncur.status=cantor(ncur.s); g[ncur.status]=0; ncur.f=h(ncur.s); if(a_star(ncur)) cout<<path; else puts("unsolvable"); return 0; }
四、IDA*
代码如下:0ms
#include <iostream> #include <queue> #include <algorithm> #include <cstdio> #include <cmath> #include <cstring> #include <cmath> #define MAXN 105 #define INF 1000000000 using namespace std; struct wwj { int pos; char x[12];//当前状态 }tmp; int top, found, st[400000], mi; int d[] = {-1, -3, 1, 3};//0位置编号的变化 char as[] = "lurd"; bool move[][4] = {{0,0,1,1}, {1,0,1,1}, {1,0,0,1}, {0,1,1,1}, {1,1,1,1}, {1,1,0,1}, {0,1,1,0}, {1,1,1,0}, {1,1,0,0}}; //估价函数:计算曼哈顿距离 int h(wwj &t) { int sum = 0; for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) { int k = i * 3 + j; if(t.x[k] == 'x') continue; sum += abs(i - (t.x[k] - '1') / 3) + abs(j - (t.x[k] - '1') % 3); } return sum; } void output() { for(int i = 1;i <= top; i++) printf("%c", as[st[i]]); printf("\n"); } //p:当前0的位置;dp:当前深度;maxdp:限制深度 void dfs(int p, int dp, int maxdp) { int f = h(tmp);//当前状态的估价函数 int i, t; if(mi > f) mi = f; if(f + dp > maxdp || found) return; if(f == 0)//到达目标状态 { output(); found = 1; return; } for(i = 0;i < 4; i++) { if(move[p][i] == 0) continue; t = d[i] + p;//t是0的位置 //一个重要的剪枝,如果之前的一步和现在的一步是相反的,无疑是做了无用功,不必搜索。 //即:状态判重 if(top > 0 && d[st[top]] + d[i] == 0) continue; swap(tmp.x[t], tmp.x[p]); st[++top] = i; dfs(t, dp + 1, maxdp); top--; swap(tmp.x[t], tmp.x[p]);//交换位置 } } void IDA() { int maxdp = h(tmp);//初始为初状态的h值 found = 0; while(found == 0) { mi = 0x7fffffff; //mi表示在当前的h值限制下所能搜到的最优位置,即h值最小的位置 top = 0; dfs(tmp.pos, 0, maxdp); maxdp += mi; //如果不能搜到就将mi加上去 } } int main() { for(int i = 0; i < 9; i++) { scanf("%s", &tmp.x[i]); if(tmp.x[i] == 'x') tmp.pos = i; } IDA(); return 0; }

浙公网安备 33010602011771号