[洛谷P2324][题解][SCOI2005]骑士精神
题目戳我
算是一个\(IDA*\)的经典题了吧
0.\(IDA*\)介绍
\(A*\):启发式搜索
\(IDS\):迭代加深搜索
我们来分别介绍。
\(A*\)是什么
\(A*\)是一种叫做启发式搜索的东西,它的主要思想是利用估价函数\(h*\)获得一个强力的剪枝
设当前状态为\(now\),我们要求最少的搜索步数,那么令从\(now\)走到结束的步数为\(h\)
显而易见,在真实情况下,我们是不能求出\(h\)的,于是我们就用\(h*\leq h\)来代替
\(IDS\)是什么
在很多问题中,我们不能确定搜索的最大步数,这样如果爆搜的话就会获得RE or TLE
于是我们就人为规定一个步数,超过了就返回(其实这里有点像BFS了),这种算法就叫做迭代加深搜索
\(IDA*\)是什么
迭代加深的时候加一个估价函数剪枝
1.思路
为什么这道题可以用\(IDA*\)呢?我们来关注两个点:
(1)题目规定步数\(\leq 15\),可以迭代加深
(2)估价函数很容易求:假设每个棋子都可以随便跳,那么\(h*\)可以设为当前与最终状态不同的棋子数-1(-1是因为要除去空格)
2.实现
我们把空格当成一个棋子走,每走一步判断一下即可
3.代码
依然省去了缺省源
const int dx[8]={2,1,-1,-2,-2,-1,1,2};
const int dy[8]={1,2,2,1,-1,-2,-2,-1};
const int ed[6][6]={\
{0,0,0,0,0,0},\
{0,1,1,1,1,1},\
{0,0,1,1,1,1},\
{0,0,0,2,1,1},\
{0,0,0,0,0,1},\
{0,0,0,0,0,0}};
int T,st[6][6],okay,sx,sy;
inline int A_star(int now[6][6]){
int cnt=0;
for(rg int i=1;i<=5;i++){
for(rg int j=1;j<=5;j++){
if(now[i][j]!=ed[i][j])cnt++;
}
}
return cnt-1;//注意-1
}
void DFS(int x,int y,int now[6][6],int limt,int cnt){
if(okay)return;
if(cnt+A_star(now)>limt)return;//A*剪枝
if(A_star(now)==-1){
okay=1;
return;
}
for(rg int i=0;i<8;i++){//枚举空格可走的位置
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=5&&yy>=1&&yy<=5){
swap(now[x][y],now[xx][yy]);
DFS(xx,yy,now,limt,cnt+1);
swap(now[x][y],now[xx][yy]);
}
}
}
int main(){
Read(T);
while(T--){
okay=0;
for(rg int i=1;i<=5;i++){
char ipt[6];
cin>>ipt+1;
for(rg int j=1;j<=5;j++){
if(ipt[j]=='*'){
st[i][j]=2;
sx=i,sy=j;
}else st[i][j]=ipt[j]-'0';
}
}
if(A_star(st)==-1){//已经是最终状态就不搜了
cout<<0<<endl;
continue;
}
for(rg int i=1;i<=15;i++){//迭代加深,只枚举到15
DFS(sx,sy,st,i,0);
if(okay){
cout<<i<<endl;
break;
}
}
if(!okay)cout<<-1<<endl;
}
return 0;
}
4.完结撒花
内容来自_ajthreac_的博客(https://www.cnblogs.com/juruoajh/),未经允许,不得转载。