[SCOI2005]骑士精神

题目:骑士精神

网址:https://www.luogu.com.cn/problem/P2324

在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士,且有一个空位。

在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。

给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:为了体现出骑士精神,他们必须以最少的步数完成任务。

image

输入格式

第一行有一个正整数T(T<=10),表示一共有T组数据。

接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。

两组数据之间没有空行。

输出格式

每组数据输出占一行。

如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

输入样例:
2
10110
01*11
10111
01001
00000
01011
110*1
01110
输出样例:
 7
-1

本题可以使用IDA*求解。

很显然,对于一个状态,对空格像8个方向进行枚举即可。

估价函数怎么运作?

对于如果当前深度 + 该状态下没有归位的棋子个数 > 所限制的深度,剪枝。

不对啊,有反例的:最开始只有一个位置与空格互相错位,并且只需1步即可变成目标状态,那么此时的估价大于实际代价,有误!

因此,估价函数中数量应该是:除空格外,未归位的个数。

这次没有反例吧!

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct rec
{
	int x, y;
} st;

const int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
const int dy[8] = {2, 1, -1, -2, 2, 1, -1, -2};
const int dc[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}};
int map[5][5];

int calc(int (*mat)[5])
{
	int cnt = 0;
	if(mat[2][2] != -1) cnt = -1;
	for(int i = 0; i < 5; ++ i)
	{
		for(int j = 0; j < 5; ++ j)
		{
			if(mat[i][j] != dc[i][j]) ++ cnt;
		}
	}
	return cnt;
}
bool valid(int (*mat)[5])
{
	for(int i = 0; i < 5; ++ i)
		for(int j = 0; j < 5; ++ j)
			if(dc[i][j] != mat[i][j]) return false;	
	return true;
}
bool dfs(int (*book)[5], rec blank, rec fa, int dep)
{
	if(calc(book) > dep) return false;
	if(!dep) return valid(book);
	int x = blank.x, y = blank.y, mat[5][5];
	memcpy(mat, book, sizeof(mat));
	rec q;
	for(int i = 0; i < 8; ++ i)
	{
		q = (rec) {x + dx[i], y + dy[i]};
		if(q.x < 0 || q.x >= 5 || q.y < 0 || q.y >= 5 || (q.x == fa.x && q.y == fa.y)) continue;
		
		swap(mat[x][y], mat[q.x][q.y]);
		if(dfs(mat, q, blank, dep - 1)) return true;
		swap(mat[x][y], mat[q.x][q.y]);
	}
	return false;
}
int main()
{
	char c[5][5];
	int T;
	scanf("%d", &T);
	while(T --)
	{		
		for(int i = 0; i < 5; ++ i) cin>> c[i];	
		for(int i = 0; i < 5; ++ i)
		{
			for(int j = 0; j < 5; ++ j)
			{
				switch(c[i][j])
				{
					case '1':
					{
						map[i][j] = 1;
						break;
					}
					case '0':
					{
						map[i][j] = 0;
						break;
					}
					case '*':
					{
						map[i][j] = -1;
						st.x = i, st.y = j;
						break;
					}
					default: break;
				}
			}
		}
		bool okay = false;
		for(int dep = 0; dep <= 15; ++ dep)
		{
			if(dfs(map, st, st, dep))
			{
				okay = true;
				printf("%d
", dep);
				break;
			}
		}
		if(!okay) puts("-1");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/zach20040914/p/12843869.html