博客园同步html
原题连接node
简要题意:c++
给定一个 的矩阵,每次能够把空格旁边(四方向)的一个位置移到空格上。求到目标状态的最小步数。web
前置知识:app
将这题做为 宽度优先搜索( ) 的模板题讲解!svg
首先,众所周知 的搜索树相似于这样:函数
其中,每一个矩形都是一个状态,上面的数字是 时间戳(即搜索编号) ,红色的表示往下搜索,绿色的表示往上回溯。优化
大体分为 步:spa
从当前状态开始,依次搜索子状态,进入第 步。code
若是当前状态 全部子状态都搜完了 或者 没有子状态,那么结束当前搜索,回溯至第 步。
找到答案即马上一层层回溯结束;不然搜完全部状态返回无解。
你会发现,对于本题,若是你用 ,你不知道深度是多少,颇有可能超时。(固然 记忆化 能够加快,但仍是容易超时)由于 是 盲目搜索,直到当前状态搜完为止。那么,极有可能把全部状态都搜一遍。(即 )这是极其危险的!
下面引出一个概念: .
求最小步数 / 最优解的状况下, 通常比 要来的优。
先给出一个搜索状态图:
你会发现, 的搜索是一层层搜的,即宽度优先的,而且没有任何回溯的过程。
并且, 存在性质:
第一次到达的必定是最优的。(这是革命的本钱,没有它 就和 降为同样效率了)
一个状态保证只搜一次。(由于第一次是最优的,后面来的都是劣的,因此直接 开哈希剪枝。)
那么你会说:咱们怎么实现呢?
用 队列来实现,步骤以下:
将开始状态入队。
取出队首做为当前状态,把全部 当前状态能扩展出去的状态 都入队,而且作好哈希。而后 当前状态出队。
找到答案马上中止搜索(由于性质 的存在能够这样作),不然搜完返回无解。
这样,先献出一段 的通常状况下的伪代码:
q.push(make_pair(x,y)); //x 是状态,y 是步数 h[x]=1; //哈希 while(!q.empty()) { int t=q.front().x , step=q.front().y; q.pop(); //取出队首,而后出队 for(/*枚举 t 能到达的状态 v*/) if(!h[v]) { h[v]=1; q.push(make_pair(v,step+1)); //哈希,入队 if(v == end) {printf("%d\n",step+1);return;} //搜到答案 } } puts(/*无解状况*/); //搜完没有答案即无解
那么,比较两种搜索,你会发现:
若是 必定把全部状况搜完 ,那么 和 同样,都是遍历一遍。
若是 深度极深,但答案并不大,那么 的 宽度优先 策略会更优。
若是 深度、宽度至关 ,那么 和 效率同样,但 的代码相对简洁。(只是相对,由于通常新手会认为 更简单,不过写熟了都同样)
若是 深度极深,宽度极宽,那么两种搜索都不优时,咱们就须要用 双向宽搜 或者 迭代加深搜索(即 ) 再或者 搜索 进行优化。(不过本题不用)
说了这么多,来研究这道题目。
状态:字符串,即矩阵。
答案:即记录步数。
哈希:用 实现字符串哈希。
队列:开结构体解决问题,写函数打包。
状态转移:将字符串的第 位(这里 )对应矩阵的 行, 列,在矩阵上转移,而后再转回字符串。
时间复杂度: ,能够经过。
解释: 是遍历状态, 是转移状态, 是由于咱们用了 .(若是用 康托展开 进行优化哈希,那么能够达到 ,会更优些)
实际得分: .
时间: .(并不快,可是最慢的一个点是 ,能够接受)
空间: .(并不大)
#pragma GCC optimize(2) #include<bits/stdc++.h> using namespace std; inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();} int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;} struct node { string s; int step; }; //const int dx[4]={1,-1,3,-3}; const int dx[4]={0,0,1,-1}; const int dy[4]={-1,1,0,0}; //四方向矩阵转移 queue<node> q; map<string,bool> h; string start,end="123804765"; inline node mp(string s,int step) { node t; t.s=s; t.step=step; return t; } //结构体打包函数 inline int find(string s) { for(int i=0;i<s.size();i++) if(s[i]=='0') return i; } //找到 0 的位置 inline void bfs() { q.push(mp(start,0)); h[start]=1; if(start==end) {puts("0");return;} //防止一开始就到终点 while(!q.empty()) { string s=q.front().s; int step=q.front().step; // cout<<s<<" "<<step<<endl; q.pop(); int wz=find(s); //找到 0 的位置 int x=wz/3,y=wz%3; for(int i=0;i<4;i++) { int nx=x+dx[i],ny=y+dy[i]; if(nx<0 || nx>2 || ny<0 || ny>2) continue; swap(s[nx*3+ny],s[wz]); //交换 if(!h[s]) { h[s]=1; q.push(mp(s,step+1)); if(s==end) {printf("%d\n",step+1);return;} } swap(s[nx*3+ny],s[wz]); //记得换回去,不要影响后面的转移 } } } int main(){ cin>>start; bfs(); //搜索 return 0; }