【题解】P2324[SCOI2005] 骑士精神
Description
在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。
在任何时候一个骑士都能按照骑
士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空
位上。给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步
数完成任务。目标状态:
Sample Input
2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100
Sample Output
7
-1
Hint
\(T \leqslant 10\)
Solution
最暴力:直接暴力动空格,复杂度 \(O(8^{step})\),有点 \(wei\)。。。
首先我们发现这题其实这个数据范围好像爆搜不大 \(wei\) ,再观察题面,发现这题说超过 \(15\) 步输出 \(-1\),emmm好像可以用迭代加深。
但是我们都知道迭代加深其实和宽搜差不了多少。。。还是过不了。
那咋的办。考虑对于所有爆搜的题大部分可以依靠 \(A*\) 获得不错的效率,当然不排除,\(A*\) 估价写 \(wei\) 丢失最优解。
关于估价函数的一些小细节:
-
估价函数最好小于等于实际的函数值,比如k短路中我们以到终点的最短路作为估价函数。
-
估价函数的构造肯定是和题目的要求答案有密不可分的关系。
举几个估价函数的小栗子:
- 迷宫: 到终点的曼哈顿距离
- K短路:到终点的最短路
- 求最小步数归位:与终点的状态差距(距离)
由于博主太 \(wei\) 了,没得办法,就会这么点东西,再去刷刷YBT找找感觉。
但是,这样貌似复杂度还是有点高,至少我是过不了,神他喵忘判走过去又走回来的情况了,T飞了。。。
Code
#include<bits/stdc++.h>
using namespace std;
#define R register
int T;
bool flag=false;
const int N=7;
const int dx[]={2,1,-1,-2,-2,-1,1,2};
const int dy[]={1,2,2,1,-1,-2,-2,-1};
const int goal[N][N]={
{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 a[N][N];
char s[N][N];
inline int h(){
int ret=0;
for(int i=1;i<=5;++i) for(int j=1;j<=5;++j)
ret+=(a[i][j]!=goal[i][j]);
return ret;
}
inline bool check(){
for(int i=1;i<=5;++i) for(int j=1;j<=5;++j)
if(a[i][j]!=goal[i][j]) return true;
return false;
}
inline void A_star(int deep,int dep,int cx,int cy,int prex,int prey){
if(dep==deep){
if(!check()) flag=true;
return;
}
for(int i=0;i<8;++i){
int x=cx+dx[i],y=cy+dy[i];
if(x<1 || x>5 || y<1 || y>5 || (x==prex&& y==prey)) continue;
swap(a[cx][cy],a[x][y]);
if(dep+h()<=15)
A_star(deep,dep+1,x,y,cx,cy);
swap(a[cx][cy],a[x][y]);
}
}
int main(){
scanf("%d",&T);
while(T--){
int sx=0,sy=0;
flag=false;
for(R int i=1;i<=5;++i) scanf("%s",s[i]+1);
for(R int i=1;i<=5;++i) for(int j=1;j<=5;++j){
if(s[i][j]=='*') a[i][j]=2,sx=i,sy=j;
else a[i][j]=s[i][j]-'0';
}
for(int i=1;i<=15;++i){
A_star(i,0,sx,sy,0,0);
if(flag==true){
printf("%d\n",i);
break;
}
}
if(flag==false) puts("-1");
}
return 0;
}