八数码问题/搜索小结
cdcq讲的挺好的其实。之所以没有回应是因为真的想不出了。
这个问题可以用BFS和A*解决。
BFS
考虑按空格的位置搜索,每次操作相当于移动空格。
此处需要记忆化,不然会T飞。
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<queue> 6 #include<map> 7 using namespace std; 8 int n; 9 int c[4][4]; 10 queue<int> q; 11 map<int,int> mp; 12 13 int k[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; 14 15 inline bool check(int x,int y){ 16 if(x<1||x>3||y<1||y>3)return false; 17 return true; 18 } 19 20 inline int rebiu(){ 21 int ans=0; 22 for(int i=1;i<=3;i++) 23 for(int j=1;j<=3;j++) 24 ans=ans*10+c[i][j]; 25 return ans; 26 }//将矩阵转化成数 27 28 inline void bfs(int now){ 29 q.push(now); 30 mp[now]=0; 31 while(!q.empty()){ 32 int lr,ud;//存储空格的位置 33 int x=q.front();q.pop(); 34 if(x==123804765){ 35 break; 36 } 37 int shp=x; 38 for(int i=9;i>=1;--i){ 39 int k=shp%10;shp/=10; 40 c[(i-1)/3+1][((i-1)%3)+1]=k; 41 if(k==0) ud=(i-1)/3+1,lr=((i-1)%3)+1; 42 }//恢复成矩阵,swap要用的 43 44 for(int i=0;i<4;++i){ 45 int dx=ud+k[i][0],dy=lr+k[i][1]; 46 if(!check(dx,dy))continue; 47 swap(c[ud][lr],c[dx][dy]); 48 int val=rebiu(); 49 if(!mp.count(val)) 50 mp[val]=mp[x]+1, 51 q.push(val); 52 swap(c[ud][lr],c[dx][dy]); 53 } 54 } 55 56 } 57 58 59 int main(){ 60 scanf("%d",&n); 61 bfs(n); 62 cout<<mp[123804765]; 63 }
双向BFS
双向BFS的特点:当前状态和目标状态都明确。
双向BFS大大降低了搜索层数,所以快的飞起。
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<queue> 6 #include<map> 7 using namespace std; 8 int n; 9 int c[4][4]; 10 map<int,int> v;//被哪个点扫到 11 map<int,int> ans; 12 queue<int> q; 13 int finl; 14 int end=123804765; 15 int dr[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; 16 17 inline int rebiu(){ 18 int ans=0; 19 for(int i=1;i<=3;i++) 20 for(int j=1;j<=3;j++) 21 ans=ans*10+c[i][j]; 22 return ans; 23 } 24 25 inline void bfs(int now){ 26 int flag=0; 27 if(now==end)return; 28 q.push(now);q.push(end); 29 v[now]=1;v[end]=2;ans[now]=0;ans[end]=1;//ans[end]是要等于1的。 30 while(!q.empty()){ 31 int x=q.front();q.pop(); 32 int k=x,px,py; 33 for(int i=9;i>=1;i--){ 34 c[(i-1)/3+1][(i-1)%3+1]=k%10; 35 if(k%10==0)px=(i-1)/3+1,py=(i-1)%3+1; 36 k/=10; 37 } 38 for(int i=0;i<4;i++){ 39 int dx=px+dr[i][0],dy=py+dr[i][1]; 40 if(dx<1||dy<1||dx>3||dy>3)continue; 41 swap(c[dx][dy],c[px][py]); 42 int val=rebiu(); 43 if(v[val]==v[x]){ 44 swap(c[dx][dy],c[px][py]); 45 continue; 46 }//利用v顺便记忆化一下 47 if(v[val]+v[x]==3){ 48 finl=ans[val]+ans[x]; 49 flag=1;break; 50 }//两个不同类型相遇 就是答案 51 v[val]=v[x],ans[val]=ans[x]+1; 52 q.push(val); 53 swap(c[dx][dy],c[px][py]); 54 } 55 if(flag)break; 56 } 57 } 58 59 60 int main(){ 61 scanf("%d",&n); 62 bfs(n); 63 cout<<finl; 64 }
A*
对于一个状态n, 经过n的答案 f* [ n ] = g* [ n ] + h* [ n ].
g*表示从出发点到n,h*表示从n到终点。
我们可以设置估价函数 f [ n ] = g [ n ] + h [ n ]
h是我们对未来的预估,且 h [ n ] <=h* [ n ]
这样可以保证求出最优解且h越大跑的越快。
对于一个题有多种估价方式,只要正确,大胆跑就完事了。
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<map> 6 using namespace std; 7 int n; 8 int brd=0; 9 int c[4][4]; 10 int finl[4][4]={{0,0,0,0},{0,1,2,3},{0,8,0,4},{0,7,6,5}}; 11 int dr[4][2]={{0,1},{1,0},{-1,0},{0,-1}};//方向需要特殊排列一下 12 int px,py; 13 int flag=0; 14 int k=1; 15 inline bool lok(int s){ 16 int dif=0; 17 for(int i=1;i<=3;i++) 18 for(int j=1;j<=3;j++) 19 if(c[i][j]!=finl[i][j]){ 20 ++dif; 21 if(dif+s>k)return false; 22 } 23 return true; 24 }/*估价函数可以更优。目前我知道最优的 25 应该是每个点与目标位置的曼哈顿距离(不算空格 26 因为空格位置在点移动过程中就改变了,不用另算) 27 */ 28 inline bool check(){ 29 for(int i=1;i<=3;i++) 30 for(int j=1;j<=3;j++) 31 if(c[i][j]!=finl[i][j])return false; 32 return true; 33 } 34 35 inline void dfs(int x,int y,int s,int pre){ 36 if(s==k){ 37 if(check())flag=1; 38 return; 39 } 40 if(flag)return; 41 for(int i=0;i<4;i++){ 42 int dx=x+dr[i][0],dy=y+dr[i][1]; 43 if(dx<1||dx>3||dy<1||dy>3||i+pre==3)continue;//i+pre是为了防止搜回去,mp存状态T了 44 swap(c[dx][dy],c[x][y]); 45 if(lok(s)) 46 dfs(dx,dy,s+1,i); 47 swap(c[dx][dy],c[x][y]); 48 } 49 } 50 51 int main(){ 52 scanf("%d",&n); 53 if(n==123804765){ 54 printf("0");return 0; 55 } 56 for(int i=8;i>=0;i--){ 57 c[i/3+1][(i%3)+1]=n%10; 58 if(n%10==0)px=i/3+1,py=(i%3)+1; 59 n/=10; 60 } 61 while(k++){ 62 dfs(px,py,0,-1); 63 if(flag){ 64 printf("%d",k); 65 return 0; 66 } 67 } 68 }
IDA*
在搜索深度不确定,目标状态不明确(有多解)时使用IDA* 。
ID==迭代加深。