传送门
文章目录
- 解析
解析
很显然,让马走的话状态记录和转移都会比较复杂
所以转化成让空位跳会更好做一点
但这不是重点
初看本题,其实第一感觉是bfs
但是状态数理论上最差可以达到815,(当然基本不可能跑满),是无法接受的
注意到,本题中出现了只需求15步以内的答案的要求,我们就想到使用迭代加深搜索,逐步扩大搜索树的深度,这样就能利用dfs的同时,保证最先出现的答案就是最优解。
但是,对于无解或答案接近15的情况来说,这个在时间复杂度上其实并没有起到太多优化的作用
因此我们需要更好的剪枝策略
可以引入一个估价函数,记为h
其意义是最好情况下,当前状况完成任务所需要的步数
再设当前步数为step,迭代的深度上限是maxstep,那么当——
step+h>maxstepstep+h>maxstepstep+h>maxstep
时,可以直接return
对于本题,估价函数可以是当前状态与目标状态(这里可以打个表)逐位比较,有不同则加一
(注意,因为当最后一步时,空位和马会同时归位,估计函数会减2,所以上面的式子应该对于本题有一个特殊的修改:
step+h>maxstep+1step+h>maxstep+1step+h>maxstep+1
通过这样剪枝,就大大减少了不必要的搜索
使本题得以解决
(本题还有一个作用颇大的剪枝策略:就是记录一下上一步跳的方向,防止搜索过程中出现来回横跳的无意义搜索)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+100;
const int mod=20040313;
int read(){int res=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}while(c>='0'&&c<='9'){res*=10;res+=c-'0';c=getchar();}return f*res;
}
int mp[8][8];
char s[150];
int t;
int dx[9]={0,-1,-2,-2,-1,1,2,2,1},dy[9]={0,-2,-1,1,2,2,1,-1,-2};
int jd[7][7]={
{0,0,0,0,0,0},
{0,1,1,1,1,1},
{0,0,1,1,1,1},
{0,0,0,-1,1,1},
{0,0,0,0,0,1},
{0,0,0,0,0,0}};
int flag=0;
int calc(){int cnt=0;for(int i=1;i<=5;i++){for(int j=1;j<=5;j++){if(mp[i][j]!=jd[i][j]) cnt++;}}return cnt;
}
bool exist(int x,int y){return x>=1&&x<=5&&y>=1&&y<=5;
}
void dfs(int step,int mx,int x,int y,int pre){int o=calc();if(o==0){printf("%d\n",step);flag=1;return;}if(o+step>mx+1) return;for(int i=1;i<=8;i++){if(i==pre) continue;int nx=x+dx[i],ny=y+dy[i];if(!exist(nx,ny)) continue;swap(mp[x][y],mp[nx][ny]);dfs(step+1,mx,nx,ny,i<=4?i+4:i-4);swap(mp[x][y],mp[nx][ny]);if(flag) return;}return;
}
int x,y;
int main(){t=read();while(t--){flag=0;for(int i=1;i<=5;i++){scanf("%s",s+1);for(int j=1;j<=5;j++){char c=s[j];if(c=='1') mp[i][j]=1;else if(c=='0') mp[i][j]=0;else mp[i][j]=-1,x=i,y=j;}}for(int k=1;k<=15;k++){dfs(0,k,x,y,0);if(flag) break;}if(!flag) printf("-1\n");}return 0;
}
/*
2
5 6
XXXXXX
XZZ..X
X.XXXX
M.....
.XG...
5 6
XXXXXX
XZZ..X
X.XXXX
M.....
X.G...*/