[SCOI2005]骑士精神——双向广搜

基情链接♂[SCOI2005]骑士精神——迭代加深

目录

一.双向BFS

1.引入

2.概念

3.伪板子及解释

4.运用hash进行标记

二.引入一道不太难的题目

1.题目

2.解题思路

3.代码(如果还不懂,可供参考)

谢谢!


一.双向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

数据范围与提示

n\leq 10

2.解题思路

既然使用双向BFS,就不难想到把输入定为起始状态,目标状态已经给出。但是这道题目的难点就在于每种状态的标记。首先我把这个矩阵转换成整型矩阵,‘*’就转成2。比如就样例一而言,我就转换成:

10110

01211

10111

01001

00000

然后在哈希的时候,我们可以运用最常用的一种办法:将矩阵转换成数字

但是如果直接转换,就是25位数,这未免也太大了。这是来观察整个矩阵,会发现最大的数字就是2,所以我们可以把它转换成三进制数,大大缩小了数字的最大值。那么最大值:3^{25}=847288609443

但是这个如果开数组的话还是会炸。所以可以想到用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;
}

谢谢!

发布了61 篇原创文章 · 获赞 32 · 访问量 8362

猜你喜欢

转载自blog.csdn.net/weixin_43908980/article/details/89950696