附上题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043
我用了两种方法AC。第一种是双向广搜 + 逆序对奇偶剪枝 + 康拓展开 。 第二种方法是打表法,先用bfs搜素出所有路径,保存。当然还有康拓展开。第二种速度快多了。
第一种 用时 1880MS 代码如下:
#include <bits/stdc++.h> #define MAX 400000 #define N 9 using namespace std; /* 双向广搜,解决八数码问题。 这题还要 进行奇偶剪枝,只有当初态和终态的逆序对和奇偶性相同才有解。 终态逆序对和为0 是偶数,所以在搜索时,某个状态是奇那么肯定不行。不必保存该状态了 */ int fac[] = {1,1,2,6,24,120,720,5040,40320,362880}; char f[]={‘r‘,‘l‘,‘u‘,‘d‘};//0,1,2,3 左右上下 int fc[4][2]={{0,1},{0,-1},{-1,0},{1,0}}; struct no { int id; short c; }dis[MAX];//记录路径 struct node { string str; int id; node (const string s1,const int t):str(s1),id(t){}; node(){ } }; queue<node> q1; queue<node> q2; int vis[MAX]; string Sstr = "123456789"; string Tstr = "123456789"; string news = "123456789"; int Tstr_id ,Sstr_id; int found = 0; int ANS_t,ANS_o; int op; //奇偶剪枝 bool pruning(string str) { int sum = 0; for (int i=0;i<N;++i) { if (str[i] == ‘9‘)continue; for (int j=i+1;j<N;++j) if (str[j]!=‘9‘ && str[j]<str[i]) sum++; } if (sum%2) return false; return true; } int kt(string a) { int ans = 0; for (int i = 0;i<N;++i) { int t =0; for (int j=i+1;j<N;++j) if (a[j] < a[i]) t++; ans+= t*fac[N-i-1]; } return ans; } bool change(string & s,int v,int i) { int x = i/3; int y = i%3; int cx = x+fc[v][0]; int cy = y+fc[v][1]; if (cx >=0 && cx < 3 && cy>=0 && cy < 3) { int j = cx*3 + cy; news = s; char t = news[j]; news[j] = news[i]; news[i] = t; //形成新状态后,检测该状态的奇偶性 if (pruning(news)) return true; else return false; } else return false; } int put (int i) { if (i==0) return 1; if (i==1) return 0; if (i==2) return 3; if (i==3) return 2; } //双向bfs 的扩展 void dbfs_expend(node & s,bool Flag,int g,int i) { if (!change(s.str,i,g)) return; int v = kt(news); if (Flag) //正向 { if (vis[v]!=1) { if (vis[v] == 2)//在队列二中出现过,也就是双在此交汇了 { ANS_t = v; ANS_o = s.id; op = i;//衔接操作 found = 1; return ; } vis[v] = 1; dis[v].c = i;dis[v].id = s.id; q1.push(node(news,v)); } } else //反向 { if (vis[v]!=2) { if (vis[v]==1) //交汇了 { ANS_t = s.id; ANS_o = v; op = put(i); found = 1; return ; } vis[v] = 2; dis[v].c = put(i);dis[v].id = s.id; q2.push(node(news,v)); } } } void dbfs() { while(!q1.empty()) q1.pop(); while(!q2.empty()) q2.pop(); node s1(Sstr,Sstr_id); node s2(Tstr,Tstr_id); q1.push(s1); //正向搜索队列 q2.push(s2); //反向搜索队列 vis[Sstr_id] = 1; vis[Tstr_id] = 2; node temp; while(!q1.empty() || !q2.empty()) { if (!q1.empty()) { temp = q1.front();q1.pop(); int g = 0; while(temp.str[g]!=‘9‘) ++g; for (int i=0;i<4;++i) { dbfs_expend(temp,true,g,i); if (found) return;//找到了 } } if (!q2.empty()) { temp = q2.front();q2.pop(); int g = 0; while(temp.str[g]!=‘9‘) ++g; for (int i=0;i<4;++i) { dbfs_expend(temp,false,g,i); if (found) return ;//找到了 } } } } void init() { memset(vis,0,sizeof(vis)); found = 0; } void putans() { if (!found) printf ("unsolvable\n"); else { char ans[MAX]; int i = 0; int j = ANS_o; while(j != Sstr_id) { ans[i++] = f[dis[j].c]; j = dis[j].id; } for (int j=i-1;j >= 0; --j) //这里注意,ans数组的存储顺序,很明显,要倒着输出 { printf ("%c",ans[j]); } //中间有个衔接步骤,别忘了输出 printf ("%c",f[op]) ; //接着从 ANS_t 到回去,把从终点出发扩散的道路输出 j = ANS_t; while(j != Tstr_id) { printf ("%c",f[dis[j].c]) ; j = dis[j].id; } printf ("\n"); } } int main () { char ch; Tstr_id = kt(Tstr); while(cin >> ch) { if (ch == ‘x‘) { Sstr[0]=‘9‘; } else Sstr[0] = ch; for (int i=1;i<=8;++i) { cin >> ch; if (ch == ‘x‘) { Sstr[i] = ‘9‘; } else Sstr[i] = ch; } if(pruning(Sstr)) { Sstr_id = kt(Sstr); init(); dbfs(); putans(); } else printf("unsolvable\n"); } return 0; } /* ullddrurdllurdruldr ullddrurdllurdruldr */
第二种方法:
代码如下
#include <bits/stdc++.h> #define MAX 400000 #define N 9 using namespace std; /* bfs + 打表预处理 + 奇偶剪枝,解决八数码问题。 这题还要 进行奇偶剪枝,只有当初态和终态的逆序对和奇偶性相同才有解。 终态逆序对和为0 是偶数,所以在搜索时,某个状态是奇那么肯定不行。不必保存该状态了 */ int fac[] = {1,1,2,6,24,120,720,5040,40320,362880}; char f[]={‘r‘,‘l‘,‘u‘,‘d‘};//0,1,2,3 左右上下 int fc[4][2]={{0,1},{0,-1},{-1,0},{1,0}}; struct no { int id; short c; no(){ id = -1;c = -1; } }dis[MAX];//记录路径 struct node { string str; int id; node (const string s1,const int t):str(s1),id(t){}; node(){ } }; queue<node> q1; string Sstr = "123456789"; string Tstr = "123456789"; string news = "123456789"; int Tstr_id ,Sstr_id; int kt(string a) { int ans = 0; for (int i = 0;i<N;++i) { int t =0; for (int j=i+1;j<N;++j) if (a[j] < a[i]) t++; ans+= t*fac[N-i-1]; } return ans; } bool change(string & s,int v,int i) { int x = i/3; int y = i%3; int cx = x+fc[v][0]; int cy = y+fc[v][1]; if (cx >=0 && cx < 3 && cy>=0 && cy < 3) { int j = cx*3 + cy; //计算出移动到的位置的下标 news = s; char t = news[j]; news[j] = news[i]; news[i] = t; return true; } return false; } int put(int i) { if (i==0) return 1; if (i==1) return 0; if (i==2) return 3; if (i==3) return 2; } void bfs() { node s1(Tstr,Tstr_id); q1.push(s1); // dis[Tstr_id].id = Tstr_id; node temp; while(!q1.empty()) { temp = q1.front();q1.pop(); int g = 0; while(temp.str[g]!=‘9‘) ++g; for (int i=0;i<4;++i) { if (!change(temp.str,i,g)) continue; int v = kt(news); if (dis[v].id == -1) { dis[v].c = put(i); dis[v].id = temp.id; q1.push(node(news,v)); } } } } void putans() { int j = Sstr_id; while(j != Tstr_id) { printf ("%c",f[dis[j].c]); j = dis[j].id; } printf ("\n"); } int main () { char ch; Tstr_id = kt(Tstr); bfs(); while(cin >> ch) { if (ch == ‘x‘) { Sstr[0]=‘9‘; } else Sstr[0] = ch; for (int i=1;i<=8;++i) { cin >> ch; if (ch == ‘x‘) { Sstr[i] = ‘9‘; } else Sstr[i] = ch; } Sstr_id = kt(Sstr); if(dis[Sstr_id].id != -1) putans(); else printf("unsolvable\n"); } return 0; } /* ullddrurdllurdruldr ullddrurdllurdruldr */
非常经典的题目,建议大家做做,有很多优秀博客。