基情链接♂[SCOI2005]骑士精神——迭代加深
目录
一.双向BFS
1.引入
广搜这种东西相信大家都很熟悉吧,相信曾经想剪枝也是快想秃头了吧。双向BFS正是一种很好的优化方法。它可以将时间复杂度折半。一般的BFS,如果搜索树的深度为L,度为K,则搜索的最坏时间复杂度为K^L,而如果我们采用双向BFS,时间复杂度则降为2*(K^(L/2)),自然可以极大的提高搜索速度。
2.概念
双向BFS,顾名思义就是从起始状态和目标状态两面同时开始一起搜,下面有伪板子。
3.伪板子及解释
大家一定首先想到了用两个队列分别从起始和结束一起搜吧,但是这有点麻烦。我们可以把它优化成一个队列,把起始状态和目标状态同时存入一个队列,就可以依次有序的从两头开始搜,只是结构体里要多定义一个能判断是从哪个方向搜过来的的bool变量。
伪板子:
//vis1是正向搜索的标记,vis2是反向搜索的标记
while (! queue.empty ()){
t = queue.front ();
queue.pop ();
foreach (t的下一个状态next){
if (当前是正向搜索){
if (vis2[next] == 1) {处理结果,搜索结束}//如果正向和反向搜索都搜索到同一状态,说明找到了最优解
if (vis1[next] == 0){
vis1[next] = 1;
queue.push (next);
}
}
else{
if (vis1[next] == 1) {处理结果,搜索结束}
if (vis2[next] == 0){
vis2[next] = 1;
queue.push (next);
}
}
}
}
4.运用hash进行标记
众所周知,搜索都是需要标记的,但是下标的定义往往很难。比如说题目给出了一个矩阵,想要标记每种状态且不与其他状态的标记重复就特别难。这时需要运用到哈希,下面这道题目就是典例。
二.引入一道不太难的题目
1.题目
在一个5*5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。
给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:
为了体现出骑士精神,他们必须以最少的步数完成任务。
输入格式
第一行有一个正整数 ,表示一共有n组数据。
接下来有n个5*5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。
输出格式
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出 。
样例输入
2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100
样例输出
7
-1
数据范围与提示
2.解题思路
既然使用双向BFS,就不难想到把输入定为起始状态,目标状态已经给出。但是这道题目的难点就在于每种状态的标记。首先我把这个矩阵转换成整型矩阵,‘*’就转成2。比如就样例一而言,我就转换成:
10110
01211
10111
01001
00000
然后在哈希的时候,我们可以运用最常用的一种办法:将矩阵转换成数字。
但是如果直接转换,就是25位数,这未免也太大了。这是来观察整个矩阵,会发现最大的数字就是2,所以我们可以把它转换成三进制数,大大缩小了数字的最大值。那么最大值:
但是这个如果开数组的话还是会炸。所以可以想到用map。
直接开一个:map<long long, edge> vis[2]
vis[0]存从起始状态开始搜索的标记,vis[1]存从目标状态开始搜索的标记;edge是结构体,有两个变量:一个用来标记,另一个用来存步数。
附哈希函数:
LL gethash(node a) {//LL即long long,word[i][j]是矩阵
LL k = 1, ans = 0;
for (int i = 1; i <= 5; i ++) {
for (int j = 1; j <= 5; j ++){
ans += k * a.word[i][j];
k *= 3;
}
}
return ans;
}
3.代码(如果还不懂,可供参考)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <map>
using namespace std;
#define LL long long
int T;
struct edge {
int step;
bool v;
};
map<LL, edge> vis[2];
int dir[8][2] = {{-2, 1}, {-1, 2}, {1, 2}, {2, 1}, {2, -1}, {1, -2}, {-1, -2}, {-2, -1}};
int goal[6][6] = {
{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},
};
struct node {
int word[10][10];
int x, y, flag;
}st, en;
LL gethash(node a) {
LL k = 1, ans = 0;
for (int i = 1; i <= 5; i ++) {
for (int j = 1; j <= 5; j ++){
ans += k * a.word[i][j];
k *= 3;
}
}
return ans;
}
int Tow_Bfs (){
queue<node> f;
for (int i = 0; i < 2; i ++)
vis[i].clear ();
f.push (st);
f.push (en);
vis[0][gethash (st)].v = vis[1][gethash (en)].v = 1;
while (! f.empty()){
node tmp = f.front();
int pos_x = tmp.x;
int pos_y = tmp.y;
f.pop();
LL t = gethash (tmp);
if (vis[!tmp.flag][t].v){
return vis[0][t].step + vis[1][t].step;
}
for (int i = 0; i < 8; i ++){
int tox = pos_x + dir[i][0];
int toy = pos_y + dir[i][1];
if (tox >= 1 && tox <= 5 && toy >= 1 && toy <= 5){
swap (tmp.word[tox][toy], tmp.word[pos_x][pos_y]);
tmp.x = tox, tmp.y =toy;
LL t1 = gethash (tmp);
if (! vis[tmp.flag][t1].v){
vis[tmp.flag][t1].v = 1;
vis[tmp.flag][t1].step = vis[tmp.flag][t].step + 1;
if (vis[tmp.flag][t1].step < 8)
f.push (tmp);
}
swap (tmp.word[pos_x][pos_y], tmp.word[tox][toy]);
tmp.x = pos_x, tmp.y = pos_y;
}
}
}
return -1;
}
int main (){
scanf ("%d", &T);
while (T --){
int k = 1;
for (int i = 1; i <= 5; i ++){
scanf ("\n");
for (int j = 1; j <= 5; j ++){
char x;
scanf ("%c", &x);
if (x == '*')
st.x = i, st.y = j, st.word[i][j] = 2;
else
st.word[i][j] = x - 48;
}
}
for (int i = 1; i <= 5; i ++)
for (int j = 1; j <= 5; j ++)
en.word[i][j] = goal[i][j];
en.x = en.y = 3;
st.flag = 0;
en.flag = 1;
int ans = Tow_Bfs ();
if (ans >= 0 && ans <= 15)
printf ("%d\n", ans);
else
printf ("-1\n");
}
return 0;
}