最近考完试了。闲下来就正好刷刷以前没刷完的搜索专题。
简单搜索就没啥好讲的啦。就是暴力bfs和dfs。
这篇博客是kuangbin搜索进阶的专题的总结
八数码问题太经典啦。通过它来学习搜索的进阶技巧就很舒服。
首先是最简单的康拓优化。
康托展开(Hash序列权值,可以用于序列打表:
int jc[10] = {1,1,2,6,24,120,720,5040,40320,362880};
int cantor(int a[])//康托展开
{
int ans = 0, k;
for (int i = 0; i < n; i++)
{
k=0;
for (int j = i+1; j < n; j++)
if (a[i] > a[j]) k++;
ans += k * jc[n-1-i];
}
return ans;
}
既然说到康托展开那就继续讲讲使用康托展开有什么需要注意的吧。
1.康托展开对于有重复元素的情况不成立,直接套用下方式子显然不对,而笔者暂时也未找到相关文献或自己想出,证明含有重复元素的情况能用类似康托展开的方法或其他简单方法求解。如果您对此有想法,恳请在下方留言区告诉我,我会非常感激。
2. longlong最多能存下20!,21!就爆了,所以说逆康托展开只在20个元素以下的排列有用。但是康托展开的值可以取模,用处还是不小的。
3. 当题目给出不一样的字符大小关系时,完全可以根据题目要求hash字符,将其抽象为数字在进行康托展开。
关于八数码问题的四种解法:
①康托展开+逆向BFS预处理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int N = 400000; struct path { char oper; int fa; }p[N]; int jc[10] = {1,1,2,6,24,120,720,5040,40320,362880}; int in[9]; int goal[]={1,2,3,4,5,6,7,8,9}; int vis[N]; int dic[4][2] = {1,0, -1,0, 0,1, 0,-1}; struct node { int maze[9]; int num; }; int cantor(int a[])//康托展开 { int ans = 0, k; for (int i = 0; i < 9; i++) { k = 0; for (int j = i+1; j < 9; j++) if (a[i] > a[j]) k++; ans += k * jc[8-i]; } return ans; } void bfs(int qin[]) { queue<node> q; vis[cantor(qin)] = 1; node st; for (int i = 0; i < 9; i++) st.maze[i] = qin[i]; p[0].fa = -1; st.num = 8; q.push(st); while (!q.empty()) { node top = q.front(); q.pop(); for (int i = 0; i < 4; i++) { int tx = top.num % 3 + dic[i][0]; int ty = top.num / 3 + dic[i][1]; if (tx < 0) continue; if (tx >= 3) continue; if (ty < 0) continue; if (ty >= 3) continue; node temp; memcpy(temp.maze, top.maze, sizeof(top.maze)); temp.num = ty*3+tx; swap(temp.maze[top.num], temp.maze[temp.num]); int ct2 = cantor(temp.maze); int ct1 = cantor(top.maze); if (!vis[ct2]) { vis[ct2] = 1; q.push(temp); if (i == 0) p[ct2].fa = ct1, p[ct2].oper = 'l'; else if (i == 1) p[ct2].fa = ct1, p[ct2].oper = 'r'; else if (i == 2) p[ct2].fa = ct1, p[ct2].oper = 'u'; else if (i == 3) p[ct2].fa = ct1, p[ct2].oper = 'd'; //cout << p[ct2].oper << endl; } } } } int main() { cin.tie(0); ios::sync_with_stdio(0); bfs(goal); char temp[9]; while (cin >> temp[0] >> temp[1] >> temp[2] >> temp[3] >> temp[4] >> temp[5] >> temp[6] >> temp[7] >> temp[8]) { for (int i = 0; i < 9; i++) if (temp[i] == 'x') in[i] = 9; else in[i] = temp[i]-'0'; int cont = cantor(in); if (!vis[cont]) cout << "unsolvable" << endl; else { while (p[cont].fa != -1) { cout << p[cont].oper; cont = p[cont].fa; } cout << endl; } } return 0; }
②A*+康托展开+逆序对判断(这个A*真的难改。大概TLE了10发才弄出来
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <algorithm> #include <cstdio> #include <string> #include <vector> #include <queue> #include <cstring> using namespace std; const int N = 400000; struct path { char oper; int fa; }p[N]; struct node { int maze[9]; int num; int g, h; friend operator < (node a1, node a2) { return a1.g+a1.h == a2.g+a2.h ? a1.g < a2.g : a1.g+a1.h > a2.g+a2.h; //return a1.g < a2.g; } }st, ed; int jc[10] = {1,1,2,6,24,120,720,5040,40320,362880}; int in[9], goal[9] = {1,2,3,4,5,6,7,8,9}; int vis[N]; int dic[4][2] = {1,0, -1,0, 0,1, 0,-1}; int cal(node temp) { int res = 0; for (int i=0;i<9;i++) { int px=(temp.maze[i]-1)/3,py=(temp.maze[i]-1)%3; res+=abs(px-i/3)+abs(py-i%3); } return res; } void output1(int k) { if (p[k].fa == -1) return ; else { output1(p[k].fa); cout << p[k].oper; } } int cantor(int a[]) { int ans = 0, k; for (int i = 0; i < 9; i++) { k = 0; for (int j = i+1; j < 9; j++) if (a[i] > a[j]) k++; ans += k * jc[8-i]; } return ans; } void Astar() { memset(vis, 0, sizeof(vis)); priority_queue<node> pq; for (int i = 0; i < 9; i++) st.maze[i] = in[i]; p[cantor(st.maze)].fa = -1; vis[cantor(st.maze)] = 1; pq.push(st); while (!pq.empty()) { node top = pq.top(); pq.pop(); int cur = cantor(top.maze); if (cur == 0) { output1(0); cout << endl; return ; } for (int i = 0; i < 4; i++) { int tx = top.num % 3 + dic[i][0]; int ty = top.num / 3 + dic[i][1]; if (tx < 0) continue; if (tx >= 3) continue; if (ty < 0) continue; if (ty >= 3) continue; node temp; memcpy(temp.maze, top.maze, sizeof(top.maze)); temp.num = ty*3+tx; swap(temp.maze[top.num], temp.maze[temp.num]); temp.h = top.h+1; int aft = cantor(temp.maze); temp.g = cal(temp); if (!vis[aft]) { vis[aft] = 1; pq.push(temp); if (i == 0) p[aft].fa = cur, p[aft].oper = 'r'; else if (i == 1) p[aft].fa = cur, p[aft].oper = 'l'; else if (i == 2) p[aft].fa = cur, p[aft].oper = 'd'; else if (i == 3) p[aft].fa = cur, p[aft].oper = 'u'; } } } } int main() { cin.tie(0); ios::sync_with_stdio(0); char temp[9]; while (cin >> temp[0] >> temp[1] >> temp[2] >> temp[3] >> temp[4] >> temp[5] >> temp[6] >> temp[7] >> temp[8]) { for (int i = 0; i < 9; i++) if (temp[i] == 'x') in[i] = 9, st.num = i; else in[i] = temp[i]-'0'; int rev = 0; for (int i = 0; i < 9; i++) for (int j = i+1; j < 9; j++) if (in[i] == 9) continue; else if (in[i] > in[j]) rev++; if (rev & 1) cout << "unsolvable" << endl; else Astar(); } return 0; }
顺便讲讲A*常见的启发策略:
1.曼哈顿距离
2.曼哈顿距离+深度
③IDA*+康托展开+逆序对判断(这其实是一个严重被低估的算法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int N = 400000; struct path { char oper; int fa; }p[N]; int jc[10] = {1,1,2,6,24,120,720,5040,40320,362880}; int in[9]; int goal[]={1,2,3,4,5,6,7,8,9}; int vis[N]; int dic[4][2] = {1,0, 0,1, 0,-1, -1,0}; char dic_rec[5] = {'d', 'r', 'l', 'u'}; int flag = 0; //end condition check char ans_rec[N]; int cal() { int res = 0; for (int i=0;i<9;i++) { if (in[i] == 9) continue; int px=(in[i]-1)/3,py=(in[i]-1)%3; res+=abs(px-i/3)+abs(py-i%3); } return res; } int cantor(int a[]) { int ans = 0, k; for (int i = 0; i < 9; i++) { k = 0; for (int j = i+1; j < 9; j++) if (a[i] > a[j]) k++; ans += k * jc[8-i]; } return ans; } void IDAstar(int limit, int depth, int x, int y, int pre) { if (flag) return ; for (int i = 0; i < 4; i++) { if (flag) return ; int tx = x + dic[i][0]; int ty = y + dic[i][1]; if (tx < 0 || tx > 2 || ty < 0 || ty > 2) continue; if (i + pre == 3) continue; //与之前的方向相反的优化剪枝 swap(in[x*3+y], in[tx*3+ty]); int manha = cal(); if (!manha) { ans_rec[depth] = dic_rec[i]; ans_rec[depth+1] = '