zoukankan      html  css  js  c++  java
  • 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
    ", ans);
            else printf("-1
    ");
        }
        return 0;
    }
  • 相关阅读:
    求10个随机数的最大值、最小值、和、平均值
    设计并编写代码自动格斗类游戏
    用while实现阶乘
    安卓第三次作业
    第二次作业
    第一次作业
    第四次作业
    dialog
    用画图的方法理解原型对象和原型链,事半功倍今晚不加班
    【学习笔记】浅析Promise函数
  • 原文地址:https://www.cnblogs.com/liuzhanshan/p/6442158.html
Copyright © 2011-2022 走看看