原题链接:https://www.luogu.com.cn/problem/P2324
题意解读:在5*5棋盘,12个0,12个1,还有一个*,0或1可以和*交换,交换的两个位置必须是类似马走日,给定初始状态,求最少经过多少步可以到达目标状态。
解题思路:要计算最少步数,首先想到BFS,但是由于每一步有8种状态,直接BFS必然超时爆内存。
题目限制在15步之内,可以采用迭代加深方式,再加上启发性剪枝,也就是IDA*算法,才能通过本题。
所谓IDA*算法, 就是在迭代加深的基础上,每次判断当前状态stat已搜索步数和最大步数的关系if(depth > maxdepth) return false时,加入一个估价函数f(stat),f(stat)表示从当前状态stat到达目标状态的最小步数,剪枝变成:
if(depth + f(start) > maxdepth) return false;
这样可以极大减少搜索的状态数。
对于本题,估价函数可以这样设计:统计每个位置与目标位置不同的个数,不考虑*所在位置的不同,因为0/1如果归位了,*自然就归位了。
100分代码:
#include <bits/stdc++.h>
using namespace std;int t;
/*
目标终点:
11111
01111
00*11
00001
00000
转化成一维:111110111100*110000100000
*/
string target = "111110111100*110000100000";
int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};//估价函数,返回当前状态与目标状态的不同位置的个数
int f(string stat)
{int res = 0;for(int i = 0; i < 25; i++){//只需要考虑24个骑士的位置是否正确,不需要考虑*的位置,因为骑士如果都归位了,*也就归位了if(stat[i] != target[i] && stat[i] != '*') res++;}return res;
}// 从start状态开始搜索,当前搜索深度为depth,最大搜索深度为maxdepth
bool dfs(string start, int depth, int maxdepth)
{if(depth + f(start) > maxdepth) return false; //剪枝,如果当前深度加上估价函数值大于最大深度,那么就不用继续搜索了if(start == target) return true;int pos = start.find('*'); //找到*的位置int x = pos / 5, y = pos % 5; //计算*的坐标for(int i = 0; i < 8; i++){int nx = x + dx[i], ny = y + dy[i]; //计算下一个位置if(nx < 0 || nx > 4 || ny < 0 || ny > 4) continue;int npos = nx * 5 + ny; //计算下一个位置的一维坐标string tmp = start;swap(tmp[pos], tmp[npos]);if(dfs(tmp, depth + 1, maxdepth)) return true;}return false;
}int main()
{cin >> t;while(t--){string s, line;for(int i = 0; i < 5; i++){cin >> line;s += line;}int maxdepth;for(maxdepth = 0; maxdepth <= 15; maxdepth++) //迭代加深{if(dfs(s, 0, maxdepth)) break;}if(maxdepth <= 15) cout << maxdepth << endl; else cout << -1 << endl;}
}