首页 > 代码库 > HDU1429--胜利大逃亡(续)(BFS+状态压缩)

HDU1429--胜利大逃亡(续)(BFS+状态压缩)

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8312    Accepted Submission(s): 2992

Problem Description

Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……
这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方。刚开始Ignatius被关在(sx,sy)的位置,离开地牢的门在(ex,ey)的位置。Ignatius每分钟只能从一个坐标走到相邻四个坐标中的其中一个。魔王每t分钟回地牢视察一次,若发现Ignatius不在原位置便把他拎回去。经过若干次的尝试,Ignatius已画出整个地牢的地图。现在请你帮他计算能否再次成功逃亡。只要在魔王下次视察之前走到出口就算离开地牢,如果魔王回来的时候刚好走到出口或还未到出口都算逃亡失败。

Input

每组测试数据的第一行有三个整数n,m,t(2<=n,m<=20,t>0)。接下来的n行m列为地牢的地图,其中包括:
. 代表路
* 代表墙
@ 代表Ignatius的起始位置
^ 代表地牢的出口
A-J 代表带锁的门,对应的钥匙分别为a-j
a-j 代表钥匙,对应的门分别为A-J
每组测试数据之间有一个空行。

Output

针对每组测试数据,如果可以成功逃亡,请输出需要多少分钟才能离开,如果不能则输出-1。

Sample Input

4 5 17 @A.B. a*.*. *..*^ c..b* 4 5 16 @A.B. a*.*. *..*^ c..b*

Sample Output

16 -1

Author

LL

思路:

一直想不通怎么处理钥匙,甚至想用BFS+DFS搜索钥匙,可这样的话,最短路径就会很难求

翻了网上的题解,看到是在标记数组上下功夫处理钥匙的问题的:

利用状态压缩记录是否获取的钥匙的情况,然后在给标记数组增加一维(相当于给状态增加了一维):获取的钥匙的情况

即 经过1,1点获取了钥匙a 与 经过1,1点获得了钥匙b 是两个不同的状态

另外,即使状态增加了一维,最先BFS到的结果也是最优的,这是由queue的性质决定的

**************

我发现BFS的题目经常实在标记数组上下功夫,还是有一定规律的

**************

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=24;
struct node
{
    int x, y, key, dist;//key为钥匙状态,dist为走过的距离
};

char mm[N][N], str[N];//mm存储地图
int flag[N][N][1 << 11], sx, sy, ans;//这里的标记数组采用了状态压缩的方法来记录钥匙
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
queue<node> q;

void bfs()
{
    while (!q.empty()) q.pop();
    node t, nt;
    t.x = sx;
    t.y = sy;
    t.key = 0;
    t.dist = 0;
    q.push(t);
    while (!q.empty()) {
        t = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            nt.dist = t.dist + 1;
            nt.key = t.key;
            nt.x = t.x + dx[i];
            nt.y = t.y + dy[i];
            if (mm[nt.x][nt.y] == ^ ) {
                ans = nt.dist;
                return ;
            }
            else if (a <= mm[nt.x][nt.y] && mm[nt.x][nt.y] <= z) {
                nt.key = t.key | (1 << (mm[nt.x][nt.y] - a));//记录钥匙类型
            }
            if (mm[nt.x][nt.y] != * && flag[nt.x][nt.y][nt.key] == 0 && nt.dist < ans) {
                flag[nt.x][nt.y][nt.key] = 1;
                if (A <= mm[nt.x][nt.y] && mm[nt.x][nt.y] <= Z) {
                    if ((nt.key & (1 << (mm[nt.x][nt.y] - A)))) //判断是否存在此类型的钥匙
                        q.push(nt);
                }
                else q.push(nt);
            }
        }
    }
}

int main()
{
    int n, m, t;
    while (scanf("%d%d%d", &n, &m, &t) != EOF) {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                mm[i][j] = *;
                memset(flag[i][j], 0, sizeof(flag[i][j]));
            }
        }
        for (int i = 1; i <= n; i++) {
            scanf("%s", str);
            for (int j = 1; j <= m; j++) {
                mm[i][j] = str[j - 1];
                if (@ == mm[i][j]) sx = i, sy = j, mm[i][j] = .;
            }
        }
        ans = t;
        bfs();
        if (ans < t) printf("%d\n", ans);
        else printf("-1\n");
    }
    return 0;
}

HDU1429--胜利大逃亡(续)(BFS+状态压缩)