UVA1601 _特殊的建图+双向BFS

原创 2018年04月15日 16:41:35

程序来自:https://blog.csdn.net/qq_29169749/article/details/51420097

题目含义:

有1-3个东西用小写'a','b','c'表示,我们需要将他们分别移动到'A','B','C'位置,他们可以同时移动,但是不能相互穿过去,类似于不能从ab ,转换为ba状态,我们要求解的是最小的步数

解读:

我们直接看这个题的解法:首先是因为每个点有五种操作方式分别是:不动、上、下、左、右,那么如果直接使用BFS的话,每个点都有3^5的操作方式,显然这样这样一定会超时的,那么我们在这里可以使用第一个优化:我们将所有的不是墙的点的所有可以到达得到位置都保存下来,那么我们这样就会减少一些不必要的尝试。

然后我们进行BFS计算,但是他们三个可以同时移动,那么这里可以使用一个三维的数组,每一维度表示一个位置上的现在所在的位置,然后就可以进行BFS了

但是我们还不是很清楚每一次的状态怎么储存?我们可以用一个结构体,将每次的位置存起来,但是这个程序中用了一个更好的储存方法:我们知道最大的格数是16*16个,也就是256个,那么我们转换为二进制表示就是8位数,那么我们可以使用24位的二进制表示啊!然后我们再进行解压缩,所以这就是很神奇的地方!

最后还是有一个可以优化的地方就是:我们既然已经只带起点和终点了,并且还是用BFS求解,那么我们为什么不用双向BFS呢,这样还能节省一定的时间。

程序:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>

using namespace std;
int w, h, n, s[5], t[5];
char dataset[40][40];
int G[300][6], color[300][300][300], dist[300][300][300], redist[300][300][300];
int deg[300];   //记录每个编号为i的空格周围可以走的步数

int dx[] = {0, -1, 1, 0, 0};
int dy[] = {0, 0, 0, -1, 1};

int ID(int a, int b, int c) {    //对状态进行编码,一个位置占8位
    return (a << 16) | (b << 8) | c;
}

bool conflict(int a, int b, int a2, int b2) {//
    return ((a2 == b2) || (a == b2 && b == a2));//移动到了同一个点上,或者是交换了位置等都是不合理的形式
}

int bfs() {
    queue<int> qf;  //记录正向bfs
    queue<int> qb;  //记录反向bfs

    dist[s[0]][s[1]][s[2]] = 0;
    dist[t[0]][t[1]][t[2]] = 1; //分别记录正反两种遍历走了多少步数

    qf.push(ID(s[0], s[1], s[2]));
    qb.push(ID(t[0], t[1], t[2]));  //起点终点分别压入队列,压入队列的时候对点进行编码

    color[s[0]][s[1]][s[2]] = 1;
    color[t[0]][t[1]][t[2]] = 2;    //分别标注正反两种遍历已经走过的,三个点同时动

    while(!qf.empty() || !qb.empty()) {
        int fnum = qf.size(), bnum = qb.size();
        while(fnum--) {
            int u = qf.front(); qf.pop();
            int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;//解码出出列状态三个小鬼的位置

            for(int i = 0; i < deg[a];i++) {
                int a2 = G[a][i];
                for(int j = 0; j < deg[b]; j++) {
                    int b2 = G[b][j];
                    if(conflict(a, b, a2, b2))  continue;

                    for(int k = 0; k < deg[c]; k++) {
                        int c2 = G[c][k];
                        if(conflict(a, c, a2, c2) || conflict(b, c, b2, c2))    continue;

                        if(color[a2][b2][c2] == 0) {
                            dist[a2][b2][c2] = dist[a][b][c] + 1;
                            color[a2][b2][c2] = 1;
                            qf.push(ID(a2, b2, c2));
                        }
                        else if(color[a2][b2][c2] == 2) {
                            return dist[a][b][c] + dist[a2][b2][c2];
                        }
                    }
                }
            }
        }

        while(bnum--) {
            int u = qb.front(); qb.pop();
            int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u & 0xff;//oxff:256 , 二进制 :11111111

            for(int i = 0; i < deg[a]; i++) {
                int a2 = G[a][i];
                for(int j = 0; j < deg[b]; j++) {
                    int b2 = G[b][j];
                    if(conflict(a, b, a2, b2))  continue;

                    for(int k = 0; k < deg[c]; k++) {
                        int c2 = G[c][k];
                        if(conflict(a, c, a2, c2) || conflict(b, c, b2, c2))    continue;

                        if(color[a2][b2][c2] == 0) {
                            dist[a2][b2][c2] = dist[a][b][c] + 1;
                            color[a2][b2][c2] = 2;
                            qb.push(ID(a2, b2, c2));
                        }
                        else if(color[a2][b2][c2] == 1) {
                            return dist[a][b][c] + dist[a2][b2][c2];
                        }
                    }
                }
            }
        }
    }
    return -1;
}

int main() {
    while(~scanf("%d%d%d", &w, &h, &n) && w) {
        getchar();
        for(int i = 0; i < h; i++)  gets(dataset[i]);
        int cnt = 0, x[300], y[300], id[20][20];    //从图中抽取出空间并求出初始状态和目标状态,
        for(int i = 0; i < h; i++)
        for(int j = 0; j < w; j++) {
            if(dataset[i][j] != '#') {
                x[cnt] = i; y[cnt] = j; id[i][j] = cnt;//记录点的编号,xy数组根据编号得到点,id根据点得到编号
                if(islower(dataset[i][j]))  s[dataset[i][j] - 'a'] = cnt;       //初始状态
                else if(isupper(dataset[i][j])) t[dataset[i][j] - 'A'] = cnt;   //目标状态
                cnt++;  //注意这里的cnt++不能偷懒在上面一行末尾,因为这样有时候cnt++会没有执行
            }
        }

        //将所有的空格提出来建一张图,而不是临时去判断五种走法是否合法
        for(int i = 0; i < cnt; i++) {  //利用空格建立图,我们一共cnt个点,然后将每个点对应的五种情况都放进G图中,deg[i]表示这个点的五种情况哪一种可以走
            deg[i] = 0;
            for(int j = 0; j < 5; j++) {
                int nx = x[i] + dx[j];  int ny = y[i] + dy[j];
                if(dataset[nx][ny] != '#')  G[i][deg[i]++] = id[nx][ny];//储存的是点的编号
            }
        }

        if(n <= 2)  { deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }//没有第三个点,添加第三个点
        if(n <= 1)  { deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }//只包含一个点,仍然满足的话,添加第二个点

        memset(dist, 0, sizeof(dist));
        memset(color, 0, sizeof(color));
        if(s[0] == t[0] && s[1] == t[1] && s[2] == t[2])    printf("0\n");
        else    printf("%d\n", bfs());
    }
    return 0;
}

这里面的建图方法很神奇,以及解题的思想,也非常值得学习....

百度脑图入门

-
  • 1970年01月01日 08:00

uva1601(双向BFS经典题)

题目描述:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51163 首先用单向BFS解决,代码如下。最后在单向基础上改进成双...
  • qq_29169749
  • qq_29169749
  • 2016-05-15 23:13:35
  • 2154

UVa 1601 - The Morning after Halloween(双向BFS版)

继续上次UVA 1601这道题单向BFS的版本,接下来向双向BFS的方向优化。
  • crazysillynerd
  • crazysillynerd
  • 2015-01-13 20:04:04
  • 2390

uva 1601万圣节后的早晨 双向bfs

第一次接触双向bfs,认真学习了别人的代码,跟大佬学习一波,双向dfs并不是节约一半的时间,他的时间当n约大的时候,节约的时间越多,所以当n很小的时候,还是用dfs比较简单一点。 双向的bfs就是在...
  • qq_36819130
  • qq_36819130
  • 2017-05-18 10:09:56
  • 104

所谓的 双向BFS

双向BFS,既然是双向的,那么就得知道起点和终点,这样,我们就可以进行双向搜索了。 但是,双向BFS是否真的可以提高效率呢?如果能,那么又能提高多少呢? 看到过一个图,说双BFS可以在BFS的基础上把...
  • I_am_a_winer
  • I_am_a_winer
  • 2015-04-03 21:04:56
  • 2148

UVA - 1601(双向BFS)

使用双向BFS最大的优点在于
  • playwfun
  • playwfun
  • 2014-10-10 13:49:52
  • 1817

uva 1601 poj 3523 Morning after holloween 万圣节后的早晨 (经典搜索,双向bfs+预处理优化+状态压缩位运算)

直接上代码,注释在代码里,注意巧妙地数据规模256 #include #include #include #include #include using namespace std; const i...
  • jc514984625
  • jc514984625
  • 2016-06-25 22:26:26
  • 406

[状态搜索] UVa1601 The Morning after Halloween 普通BFS写法

题目:https://cn.vjudge.net/problem/UVA-1601代码:(因为是例题,大概就是访着LRJ写的)#include &quot;stdafx.h&quot; #includ...
  • icecab
  • icecab
  • 2018-03-27 18:22:27
  • 21

Uva1601双向BFS

//双向BFS #include #include #include #include #include using namespace std; const int N = 20; cons...
  • jc514984625
  • jc514984625
  • 2016-06-27 18:01:29
  • 188

双向BFS及优化

单向BFS只从起点一端开始搜索,双向BFS则是从起点和终点两边扩展节点,当节点发生重合时即找到最优解。 假设起点到终点深度为d,每个节点平均有n个分支,那么单向BFS需要扩展的节点个数为。而从起点终点...
  • ww32zz
  • ww32zz
  • 2016-02-27 16:33:04
  • 1275
收藏助手
不良信息举报
您举报文章:UVA1601 _特殊的建图+双向BFS
举报原因:
原因补充:

(最多只允许输入30个字)