P1379 八数码难题
题目大意:给你一个矩阵,问最少要几步将矩阵变为123804765,每次操作可将0与上下左右各个数调换。
思路:这道题深搜深度非常大,广搜分类太多,导致无论深搜还是广搜都会TLE,那么我们应该找一个综合广搜和深搜优点的算法——迭代加深搜索。我们枚举移动几次会到达目标节点,一旦搜到次数大于枚举的数就return。
但裸的迭代加深搜索可能过不了,所以这时就需要一个更强力的做法:IDA*。在我理解,它其实很像对迭代加深搜索的剪枝:你预估一下当前情况到目标情况所需的步数,如果这个步数加上你已经枚举的深度是大于迭代的值的话就return;而这个预估的值就是h函数,他必须要比实际值小,但越接近实际值越好,所以IDA*的搜索复杂度取决于h函数的设定。
这里我们设当前矩阵的点和目标矩阵所对应的点不同的个数为h函数,易证这个函数是正确的,但却不是最优的,复杂度O(可过),800多ms跑得飞快。
代码如下:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstdlib> #include<cstring> #include<queue> using namespace std; const int MaxN=10; int n[MaxN][MaxN],a[MaxN][MaxN],nx,ny,now; int ffx[5]={0,1,-1,0,0}, ffy[5]={0,0,0,1,-1}; int calh(){ int sum=0; for(int i=1;i<=3;++i){ for(int j=1;j<=3;++j){ if(a[i][j]!=n[i][j]) ++sum; } } return sum; } int dfs(int xx,int yy,int x,int y,int id){ if(now+calh()-1>id) return 0;//减去空格移动的1 if(calh()==0) return 1; for(int i=1;i<=4;++i){ int dx=x+ffx[i],dy=y+ffy[i]; if(dx<=0||dx>3||dy<=0||dy>3||(xx==dx&&yy==dy)) continue; ++now;swap(a[x][y],a[dx][dy]);if(dfs(x,y,dx,dy,id)) return 1; now--;swap(a[x][y],a[dx][dy]); } return 0; } int main(){ char c[MaxN]; n[1][1]=1;n[1][2]=2;n[1][3]=3; n[2][1]=8;n[2][2]=0;n[2][3]=4; n[3][1]=7;n[3][2]=6;n[3][3]=5; for(int i=1;i<=9;++i) cin>>c[i]; a[1][1]=c[1]-'0';a[1][2]=c[2]-'0';a[1][3]=c[3]-'0'; a[2][1]=c[4]-'0';a[2][2]=c[5]-'0';a[2][3]=c[6]-'0'; a[3][1]=c[7]-'0';a[3][2]=c[8]-'0';a[3][3]=c[9]-'0'; for(int i=1;i<=3;++i) for(int j=1;j<=3;++j) if(a[i][j]==0) nx=i,ny=j; for(int i=1;i<=1e5;++i){ now=0; if(dfs(0,0,nx,ny,i)) break; } printf("%d\n",now); }