题目描述
输入格式
第一行有一个正整数 T T T( T ≤ 10 T \le 10 T≤10),表示一共有 T T T 组数据。
接下来有
T
T
T 个
5
×
5
5 \times 5
5×5 的矩阵,0
表示白色骑士,1
表示黑色骑士,*
表示空位。两组数据之间没有空行。
输出格式
对于每组数据都输出一行。如果能在
15
15
15 步以内(包括
15
15
15 步)到达目标状态,则输出步数,否则输出 -1
。
样例 #1
样例输入 #1
2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100
样例输出 #1
7
-1
提示
解题思路:
启发式搜索其实就是有预测的暴力搜索,接下来介绍一下怎么预测
估价函数:
对于题中问题,最优解是每次移动都能将一个骑士归位,但这往往不可能做到
通过估价函数计算当前最优解需要几步,如果最优解都会超出限制,那么其余方法均不可能做到
//估价函数
int evaluate() {
int cnt = 0;
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= column; j++) {
if (map[i][j] != 2 && goal[i][j] != map[i][j]) {
cnt++;
}
}
}
return cnt;
}
注意空格不需要累计,否则会出现一些问题,之后具体介绍会有什么问题
根据估价函数,启发搜索的大致思路如下
void ida_dfs(int step, const int max_step) {//step代表当前操作了几步
//结束判断
//递归主体
for (/* 遍历每一种可能 */) {
//前置操作
if (step + 1 + evaluate() <= max_step)//预测
ida_dfs(step + 1);
//返回后操作
}
}
还有要补充的一点就是在启发式搜索的时候,不要一次性把max_step
的值提高到
15
15
15
那样会因为递归过深而TLE
最后,先给出AC代码,然后说明如果在估价的时候将空格也计入会怎么样
#include <iostream>
using namespace std;
const int row = 5;
const int column = 5;
const int max_step = 15;
const int goal[row + 1][column + 1] = {//目标图
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 map[row + 1][column + 1];//当前图
const int try_step[8][2] = {//尝试方向
{1, 2},{1, -2},{-1, 2},{-1, -2},
{2, 1},{2, -1},{-2, 1},{-2, -1}
};
int blank_x, blank_y, ans;
//交换函数
void swap(int& x, int& y) {
int t = x;
x = y;
y = t;
}
//估价函数
int evaluate() {
int cnt = 0;
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= column; j++) {
if (map[i][j] != 2 && goal[i][j] != map[i][j]) {
cnt++;
}
}
}
return cnt;
}
//越界判断
inline bool is_safe(int x, int y) {
return !(x <= 0 || x > row || y <= 0 || y > column);
}
//启发式搜索
void ida_dfs(int x, int y, const int max_step, int step) {
//停止搜索
if (step == max_step) {
if (!evaluate())
ans = 1;
return;
}
for (int i = 0; i < 8; i++) {
int t_x = x + try_step[i][0];
int t_y = y + try_step[i][1];
if (!is_safe(t_x, t_y)) continue;
swap(map[x][y], map[t_x][t_y]);
if (step + 1 + evaluate() <= max_step)//预测
ida_dfs(t_x, t_y, max_step, step + 1);
swap(map[x][y], map[t_x][t_y]);//回溯
}
}
int main() {
int n;
cin >> n;
char c;
while (n--) {
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= column; j++) {
cin >> c;
if (c == '*') map[i][j] = 2;
else map[i][j] = c - '0';
if (map[i][j] == 2) {
blank_x = i;
blank_y = j;
}
}
cin.ignore();
}
ans = 0;
for (int i = 0; i <= max_step; i++) {
ida_dfs(blank_x, blank_y, i, 0);
if (ans) {
cout << i << endl;
break;
}
}
if (!ans) cout << -1 << endl;
}
return 0;
}
我们知道,估价函数的作用是用当前的最优解去判断搜索继续进行下去是否还有意义
当把空格计入时,试想如果最后还差一步就达到目标时,估价函数的返回值是什么
没错,是 2 2 2
这时候估价函数不是最优解了,因为其实我们可以一次归位两个元素:一个空格,一个骑士
那么再具体到输出上会出现什么问题呢
先看一看我们的启发式搜索得出结果的条件
//停止搜索
if (step == max_step) {
if (!evaluate())
ans = 1;
return;
}
是要求step == max_step && evaluate() == 0
那么这时候我们向前回溯一步
if (step + 1 + evaluate() <= max_step)//预测
ida_dfs(t_x, t_y, max_step, step + 1);
这里的step
值为max_step - 1
,evaluate()
的返回值为
0
0
0
似乎也没什么问题?
再回溯一步,来到上一层的此处
if (step + 1 + evaluate() <= max_step)//预测
ida_dfs(t_x, t_y, max_step, step + 1);
这里的step
值是max_step - 2
,evaluate()
的返回值为
2
2
2
是不是发现了很严重的问题?这里的判断条件是不成立的,也就是说我们根本得不出搜索结果
所以在采用估价函数时一定要清楚函数返回值的意义,这篇题解到这里就结束啦=v=