[模板A*][SCOI2005]骑士精神(A*,IDA*)

在这里插入图片描述
输入格式
第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

输出格式
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

这题是一道比较好的\(A*\)的模板题,\(A*\)对dfs的优化一般叫\(IDA*\)

首先,\(A*\)有一个定义式叫做:

\(f(n)=g(n)+h(n)\)

\(f(n)\)即为一点的估价函数,\(g(n)\)是这一点实际所用的步数(价值),\(h(n)\)是对未来的所需步数(代价)的完美预测(接近完美)。

通过这个定义式,只要有h(n),每次都可以算出一个点的估价,\(A*\)算法就是通过限制估价,使得在搜索的时候不要在无意义的道路上浪费时间。即,每次限制每个点的估价\(maxdep\),一旦估价超过,就停止搜索。

根据定义式,\(h(n)\)一定要\(<=\)实际的所需步数。

在这里插入图片描述
像这张图,如果\(h(A)\)估大了的话,\(f(A)\)也会相应变大,这样\(f(A)\)有可能会被maxdep给去掉,从而找不到正确答案。

所以,为了保证答案的正确性,\(h(A)\)一定要在\(<=\)实际的所需步数的前提下尽量的大,也就是尽量估得准。

那回到这题,题目要求在15步以内完成才输出步数,否则-1,所以可以将maxdep从0到15枚举,一旦成功找到,那就是答案(最少步数)。

而最优的就是每一次都将不在相应位置上的棋子移到相应的位置上,所以\(h(A)\)可以是不匹配的棋子数,因为每次最多还原一个,所以\(h(A)\)一定是\(<=\)实际的所需步数,正确性得到保证。

\(h(A)\)

int value()
{
	int ans=0;
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
		{
			if(standard[i][j]!=tu[i][j])
			{
				ans++;
			}
		}
	}
	return ans;
}

然后就是时间的剪枝,题目要的是最少步数,所以肯定不能做回头路,还有就是没有找到完成的路径,满足这两点,加上maxdep的限制,才能继续往下搜。

其他就是一个暴力搜索了,没什么好讲的:

#include<bits/stdc++.h>
using namespace std;
int standard[5][5]={{1,1,1,1,1},{0,1,1,1,1},{0,0,-1,1,1},{0,0,0,0,1},{0,0,0,0,0}},tu[5][5],f[8][2]={{-2,-1},{-2,1},{1,2},{1,-2},{-1,2},{-1,-2},{2,-1},{2,1}},is,t,xx,yy;
char ch;
int value()//估价函数
{
	int ans=0;
	for(int i=0;i<5;i++)
	{
		for(int j=0;j<5;j++)
		{
			if(standard[i][j]!=tu[i][j])
			{
				ans++;
			}
		}
	}
	return ans;
}
void dfs(int dep,int x,int y,int maxdep,int lastway)//直接搜
{
	if(dep==maxdep)
	{
		if(!value())
		{
			is=1;
		}
		return;
	}
	for(int i=0;i<8;i++)
	{
		int xx=x+f[i][0];
		int yy=y+f[i][1];
		if(xx<0||xx>4||yy<0||yy>4||7-i==lastway)//不走出格,不走回头路
		{
			continue;
		}
		swap(tu[x][y],tu[xx][yy]);
		if(dep+value()<=maxdep&&!is)//估价函数的限制
		{
			dfs(dep+1,xx,yy,maxdep,i);
		}
		swap(tu[x][y],tu[xx][yy]);
	}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		is=0;
		for(int i=0;i<5;i++)
		{
			for(int j=0;j<5;j++)
			{
				cin>>ch;
				if(ch=='*')
				{
					tu[i][j]=-1;
					xx=i;
					yy=j;
				}else{
					tu[i][j]=ch-'0';
				}
			}
		}
		if(!value())
		{
			puts("0");
			return 0;
		}
		for(int i=1;i<=15;i++)//A*
		{
			dfs(0,xx,yy,i,9);
			if(is)
			{
				printf("%d\n",i);
				break;
			}
		}
		if(!is)//没有
		{
			puts("-1");
		}
	}
	return 0;
}
posted @ 2019-12-28 10:57  ezoi_ly  阅读(200)  评论(0编辑  收藏  举报