题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式 输入初始状态,一行九个数字,空格用0表示
输出格式 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
输入 283104765
输出 4
思路 容易想到用bfs来搜索,那么怎么记录下每一种状态是否被搜索到了呢? 思考1:每一个状态用一个字符串存储,要9个字节,需要的空间太大了。 思考2:每一个状态用一个int来存储,因为$87654321<2^{31}$,但是还是太大了。 思考3:注意到总共的状态数目为9!个,可以用康托展开
来得到每一个状态对应的hash值,这样就能过了。 思考4:其实,stl中的unordered_map
(哈希表实现)同样可以完成这个任务,更加的方便,所花费的空间和方案3差不多。
思考4:代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <unordered_map> #include <queue> using namespace std;queue<string> q; string e = "123804765" ; int dx[] = {-1 , 0 , 1 , 0 }, dy[] = {0 , 1 , 0 , -1 };unordered_map<string, int > h; void bfs (string s) { h[s] = 0 ; q.push (s); while (!q.empty ()){ string f = q.front (); if (f == e){ cout << h[e]; return ; } q.pop (); int k = f.find ('0' ); int x = k /3 , y = k % 3 ; int dist = h[f]; for (int i = 0 ; i < 4 ; i++){ int a = x + dx[i], b = y + dy[i]; if (a >= 0 && a <= 2 && b >=0 && b <= 2 ){ swap (f[k], f[a * 3 + b]); if (!h.count (f)){ q.push (f); h[f] = dist + 1 ; } swap (f[k], f[a * 3 + b]); } } } cout << "-1" ; } int main () { string s; char c; for (int i = 0 ; i < 9 ; i++){ cin >> c; s += c; } bfs (s); }
题目描述 在一个 3×3的网格中,1∼8 这 8 个数字和一个 X
恰好不重不漏地分布在这 3×3的网格中。
例如:
在游戏过程中,可以把 X
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
例如,示例中图形就可以通过让 X
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 X 4 6 4 X 6 4 5 6 4 5 6 7 5 8 7 5 8 7 X 8 7 8 X
把 X
与上下左右方向数字交换的行动记录为 u
、d
、l
、r
。
现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。
输入格式 输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
则输入为:1 2 3 x 4 6 7 5 8
输出格式 输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。
如果答案不唯一,输出任意一种合法方案即可。
如果不存在解决方案,则输出 unsolvable
。
输入
输出
思路 基本想法同上,但还可以优化
思考5:A*算法
A* 使用A*算法需注意一下几点:
估计距离一定要小于等于实际距离。这道题中估计距离为,每个数字到它应该到的位置的曼哈顿距离之和。
除终点外,所有点在出队时,不能保证找到最短路径
需要保证有解,否则算法复杂度会退化为朴素解法。对于这道题,可以证明有解的充要条件为:逆序对数量为偶数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <iostream> #include <vector> #include <cstring> #include <algorithm> #include <math.h> #include <queue> #include <deque> #include <unordered_map> using namespace std;typedef pair<int ,int > PII;typedef pair<int ,string> PIS;int f (string state) { int res = 0 ; for (int i = 0 ; i < state.size (); i ++ ) if (state[i] != 'x' ) { int t = state[i] - '1' ; res += abs (i / 3 - t / 3 ) + abs (i % 3 - t % 3 ); } return res; } string bfs (string start) { unordered_map<string,int > dist; unordered_map<string,pair<string,char > >prev; priority_queue<PIS,vector<PIS>,greater<PIS> >hp; string ed="12345678x" ; int dx[4 ] = {-1 , 0 , 1 , 0 }, dy[4 ] = {0 , 1 , 0 , -1 }; char op[4 ] = {'u' , 'r' , 'd' , 'l' }; hp.push ({f (start), start}); dist[start] = 0 ; while (hp.size ()) { PIS t = hp.top (); hp.pop (); string state = t.second; if (state == ed) break ; int step = dist[state]; int x, y; for (int i = 0 ; i < state.size (); i++) if (state[i] == 'x' ) { x = i / 3 , y = i % 3 ; break ; } string source = state; for (int i = 0 ; i < 4 ; i++) { int a = x + dx[i], b = y + dy[i]; if (a < 0 || a >= 3 || b < 0 || b >= 3 ) continue ; swap (state[x * 3 + y], state[a * 3 + b]); if (!dist.count (state) || dist[state] > step + 1 ) { dist[state] = step + 1 ; prev[state] = {source, op[i]}; hp.push ({dist[state] + f (state), state}); } swap (state[x * 3 + y], state[a * 3 + b]); } } string res; while (ed != start) { res += prev[ed].second; ed = prev[ed].first; } reverse (res.begin (), res.end ()); return res; } int main () { string g,seq; char c; while (cin>>c){ g+=c; if (c!='x' ) seq+=c; } int cnt=0 ; for (int i=0 ;i<8 ;i++){ for (int j=i+1 ;j<9 ;j++){ if (seq[j]<seq[i]) cnt++; } } if (cnt%2 ){ cout<<"unsolvable" <<endl; return 0 ; } cout<<bfs (g)<<endl; return 0 ; }