[洛谷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.完结撒花

posted @ 2020-04-11 11:10  ajthreac  阅读(217)  评论(1编辑  收藏  举报