洛谷 P2324 [SCOI2005]骑士精神 题解
题目
题目描述

输入输出格式
输入格式:
第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。
输出格式:
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。
输入输出样例
说明

题目大意
有一个棋盘,上面有一些黑马和白马,问最少多少步能到达目标状态
思路
一道有点难(也许只有我这么认为)的搜索题目
题目说只要求15步之内的最小步数,超出了就不用算
可以从1到15枚举(或二分)答案,用dfs判断答案是否可行
在这题中,考虑空格的位置来搜索比较方便(毕竟只有一个空格)
考虑剪枝
1: 在搜索时记录与目标状态不同的不同的方格个数,因为除了最后一步可以一步将两个不正确的格子变为正确,其他时候每一步都只能将一个格子更正
所以如果 当前已经走的步数 + 状态不对的格子数 - 1 > 限定的答案 直接return (注意:这里一定要-1)
if (now + ss - 1 > maxn) return false; //剪枝 1 :如果当前预计的最少步数已经超过限定的值,停止 //由于最后一步可以一下更正两格,所以最少步数为 now + ss - 1
2:不走回头路,如果走回去就continue
在实现时将八个方向按照一定顺序搜索,进行判断
int dx[8] = {-2, -2, -1, 1, -1, 1, 2, 2}, dy[8] = {-1, 1, 2, 2, -2, -2, -1, 1};
//dx,dy按照一定的顺序来,便于判断是否走回去
for (int i = 0; i < 8; i++) //8个方向搜索
{
if (i + last == 7) continue; //如果走第i个方向是往回走就不用搜索了(这就是为什么dx和dy要有一定顺序)
}
搜索方法
空格每次回与一个白马或黑马交换位置:
考虑马
情况1:如果一个马原本在正确的位置,交换后位置错误,则状态不同的格子数 + 1;
情况2:和情况1相反,一个马从错误位置换到了正确位置,则状态不同的格子数 - 1;
考虑空格
情况3:如果一个空格原本在正确的位置,交换后位置一定错误,则状态不同的格子数 + 1;
情况4:和情况3相反,一个空格从错误位置换到了正确位置,则状态不同的格子数 - 1;
if (ch[xx][yy] == m[xx][yy] and ch[xx][yy] != m[x][y]) tmp++; //上述情况1 else if (ch[xx][yy] != m[xx][yy] and ch[xx][yy] == m[x][y]) tmp--; //情况2 if (m[x][y] == '*') tmp++; //情况3 else if (m[xx][yy] == '*') tmp--; //情况4
完整代码
#include <bits/stdc++.h>
using namespace std;
int t, sx, sy, s, maxn, ans;
int dx[8] = {-2, -2, -1, 1, -1, 1, 2, 2}, dy[8] = {-1, 1, 2, 2, -2, -2, -1, 1};
//dx,dy按照一定的顺序来,便于判断是否走回去
char ch[5][5];
char m[5][5]={ {'1', '1', '1', '1', '1'},
{'0', '1', '1', '1', '1'},
{'0', '0', '*', '1', '1'},
{'0', '0', '0', '0', '1'},
{'0', '0', '0', '0', '0'}};
//打表便于对比状态
bool ok (int x, int y, int now, int ss, int last)
//x,y表示坐标,now表示当前步数,ss表示预计最少步数,last表示上一次走的方向
{
if (now + ss - 1 > maxn) return false; //剪枝 :如果当前预计的最少步数已经超过限定的值,停止
//由于最后一步可以一下更正两格,所以最少步数为 now + ss - 1
if (!ss) {ans = now; return true;} //如果当前状态就是目标状态,记录答案,返回true
for (int i = 0; i < 8; i++) //8个方向搜索
{
int tmp = ss;
if (i + last == 7) continue; //如果走第i个方向是往回走就不用搜索了(这就是为什么dx和dy要有一定顺序)
int xx = x + dx[i], yy = y + dy[i];
if (xx < 0 or xx > 4 or yy < 0 or yy > 4) continue;
if (ch[xx][yy] == m[xx][yy] and ch[xx][yy] != m[x][y]) tmp++; //上述情况1
else if (ch[xx][yy] != m[xx][yy] and ch[xx][yy] == m[x][y]) tmp--; //情况2
if (m[x][y] == '*') tmp++; //情况3
else if (m[xx][yy] == '*') tmp--; //情况4
swap (ch[x][y], ch[xx][yy]);
bool k = ok (xx, yy, now + 1, tmp, i);
if (k) return true;
swap (ch[x][y], ch[xx][yy]); //回溯
}
return false;
}
int main()
{
scanf ("%d", &t);
while (t--)
{
s = 0, ans = -1;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++) {
cin >> ch[i][j];
if (ch[i][j] == '*') sx = i, sy = j;
if (ch[i][j] != m[i][j]) s++; //如果和目标不同,统计加 1
}
for (int i = s; i <= 15; i++){
maxn = i;
if (ok (sx, sy, 0, s, 10)) break;
}
printf ("%d\n", ans);
}
return 0;
}
点一下左边的推荐吧!谢谢~~~

浙公网安备 33010602011771号