SCOI2005-骑士精神
chunlvxiong的博客
题目描述:
给出一个5*5的棋盘,每个骑士可以走日字走到空格位置,问最少几步形成如下局面。
如果最少步数超过15步,输出-1。
思考&分析:
搜索无非也就是深搜或广搜,如果广搜的话由于总状态数为25*C(24,12)=67603900,每个状态存5*5的棋盘的话,空间是炸飞的,所以你得考虑深搜。
这里用IDA*可以达到较好的效果。
所谓IDA*,也就是迭代加深与A*的结合:
1、迭代加深:
由于可能出现这样的情况:答案很浅,搜索树很庞大,但是你却往错误的方向一直搜索,导致你搜到答案的时间很迟。
对于这种情况,你先二分一个深度k,保证搜索深度不超过这个k,看看能不能搜到,如果能那么将深度调小,否则将深度调大。
(本题由于k≤15,所以枚举和二分耗时差不多)。
2、A*
A*原本是广搜的东西,但在深搜中也可以应用,就是先往距离目标近的方向去搜。
本题的估价函数g(x):对于当前的局面,与最终局面不一样的位置个数。
为什么选择这个估价函数呢?主要是因为计算这个估价函数的代价很低:直接利用上一状态的估价函数,计算代价O(1)。
(本题比较奇怪,不加A*跑出来100ms,加A*跑出来200ms)。
所以大致做法是:
1、二分深度k并验证
2、如果当前状态的g(x)=0,那么说明找到解并返回
3、这个剪枝上面没提到:在本题中由于一次移动最多调整一个位置,所以对于某个状态,至少需要g(x)-1步(因为空格的存在)进行调整,也就是说,如果step+g(x)-1>k,那么说明一定搜不到了。
4、对于当前状态转移到下一状态(最多8个),并计算下一状态的g(x)。(如果采取A*做法就是把所有下一状态的g(x)排个序,然后先搜小的)
5、还原状态
贴代码:
不加A*:
#include <bits/stdc++.h> using namespace std; const int dx[8]={-2,-2,-1,-1,2,2,1,1}; const int dy[8]={-1,1,-2,2,-1,1,-2,2}; const char goal[5][5]={'1','1','1','1','1', '0','1','1','1','1', '0','0','*','1','1', '0','0','0','0','1', '0','0','0','0','0'}; char Map[5][5]; int Max_step; bool IDA(int x,int y,int step,int g){ int new_x,new_y,new_g,cnt=0; if (!g) return true; if (step+g-1>Max_step) return false; for (int i=0;i<8;i++){ new_x=x+dx[i],new_y=y+dy[i]; if (new_x<0 || new_x>4 || new_y<0 || new_y>4) continue; new_g=g; new_g-=(Map[x][y]!=goal[x][y]),new_g-=(Map[new_x][new_y]!=goal[new_x][new_y]); swap(Map[x][y],Map[new_x][new_y]); new_g+=(Map[x][y]!=goal[x][y]),new_g+=(Map[new_x][new_y]!=goal[new_x][new_y]); if (IDA(new_x,new_y,step+1,new_g)){ swap(Map[x][y],Map[new_x][new_y]); return true; } swap(Map[x][y],Map[new_x][new_y]); } return false; } int main(){ int T; scanf("%d",&T); while (T--){ int G=0,kx,ky; for (int i=0;i<5;i++) for (int j=0;j<5;j++){ Map[i][j]=getchar(); while (Map[i][j]!='0' && Map[i][j]!='1' && Map[i][j]!='*') Map[i][j]=getchar(); G+=(Map[i][j]!=goal[i][j]); if (Map[i][j]=='*') kx=i,ky=j; } int L=1,R=15,ans=-1; while (L<=R){ Max_step=(L+R)>>1; if (IDA(kx,ky,0,G)) R=Max_step-1,ans=Max_step; else L=Max_step+1; } printf("%d\n",ans); } return 0; }
加A*:
#include <bits/stdc++.h> using namespace std; const int dx[8]={-2,-2,-1,-1,2,2,1,1}; const int dy[8]={-1,1,-2,2,-1,1,-2,2}; const char goal[5][5]={'1','1','1','1','1', '0','1','1','1','1', '0','0','*','1','1', '0','0','0','0','1', '0','0','0','0','0'}; char Map[5][5]; int Max_step; struct node{ int x,y; }state[20][10]; bool cmp(node a,node b){ return a.y<b.y; } bool IDA(int x,int y,int step,int g){ int new_x,new_y,new_g,cnt=0; if (!g) return true; if (step+g-1>Max_step) return false; for (int i=0;i<8;i++){ new_x=x+dx[i],new_y=y+dy[i]; if (new_x<0 || new_x>4 || new_y<0 || new_y>4) continue; new_g=g; new_g-=(Map[x][y]!=goal[x][y]),new_g-=(Map[new_x][new_y]!=goal[new_x][new_y]); swap(Map[x][y],Map[new_x][new_y]); new_g+=(Map[x][y]!=goal[x][y]),new_g+=(Map[new_x][new_y]!=goal[new_x][new_y]); state[step][cnt].x=i,state[step][cnt++].y=new_g; swap(Map[x][y],Map[new_x][new_y]); } sort(state[step],state[step]+cnt,cmp); for (int i=0;i<cnt;i++){ new_x=x+dx[state[step][i].x],new_y=y+dy[state[step][i].x]; swap(Map[x][y],Map[new_x][new_y]); if (IDA(new_x,new_y,step+1,state[step][i].y)){ swap(Map[x][y],Map[new_x][new_y]); return true; } swap(Map[x][y],Map[new_x][new_y]); } return false; } int main(){ int T; scanf("%d",&T); while (T--){ int G=0,kx,ky; for (int i=0;i<5;i++) for (int j=0;j<5;j++){ Map[i][j]=getchar(); while (Map[i][j]!='0' && Map[i][j]!='1' && Map[i][j]!='*') Map[i][j]=getchar(); G+=(Map[i][j]!=goal[i][j]); if (Map[i][j]=='*') kx=i,ky=j; } int L=1,R=15,ans=-1; while (L<=R){ Max_step=(L+R)>>1; if (IDA(kx,ky,0,G)) R=Max_step-1,ans=Max_step; else L=Max_step+1; } printf("%d\n",ans); } return 0; }