hdu 杭电1429 胜利大逃亡(续)

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

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

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

不难看出,本题是一道搜索,而且本题需要记录钥匙的携带情况以判断门是否可以通过的问题
但是,有10种钥匙,这使维度处理变得困难(因为就算是开了10个维度记录钥匙也不能直接让某个维度改变,因为c++里就没有这种操作)
所以使用状态压缩

把10把钥匙的携带状态用一个二进制的数字记录下来(程序里用10进制整形变量表示)
例:第0位记录钥匙a的携带状态,0代表没有,1代表有。以此类推。
这样就可以带着10把钥匙的状态进行搜索了。
当遇到一把钥匙时:找到钥匙对应的位,然后和当前状态取“位或”操作就相当于捡起了这把钥匙
(位或:即按位取或,有1就得1,都不是1就得0)
例:当前状态有钥匙b,e;要捡起钥匙j
所以当前的状态:

abcdefghij
0100100000

在程序里就是18(2 ^ 1 + 2 ^ 4)
现在要捡起的钥匙j就是512(其他位置都是0,第九位是1)

abcdefghij
0000000001


两个二进制数字取位或之后

abcdefghij
0100100001

这样就算是捡起了钥匙。

同理,当遇到门时,判断是否有这个门对应的钥匙时
算出门对应的二进制数字,和当前状态取“位与”操作后,看得数是否为零,为零就是没有对应的钥匙。
(位与:按位取“与”,都为1得1,否则为0)

关于广搜:
先判断当前状态,再看能不能去,一定要有条理,否则会很乱。
并且广搜第一次到一个地方时一定是最小的步数(但深搜不是),所以再加个判断就行了

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

using namespace std;

char a[25][25];
int n, m, t;
int f[25][25][2048];
int sx, sy;
int addx[5] = {0, -1, 0, 1, 0};
int addy[5] = {0, 0, 1, 0, -1};

struct point
{
    int x;
    int y;
    int state;
};

queue <point> q;

int bfs()
{
    int size;
    point g, h;
    g.x = sx;
    g.y = sy;
    g.state = 0;
    f[g.x][g.y][g.state] = 0;
    q.push(g);
    while (q.size())
    {
        g = q.front();
        q.pop();
        for (int i = 1; i <= 4; i++)
        {
            h.x = g.x + addx[i];
            h.y = g.y + addy[i];
            h.state = g.state;
            if (a[h.x][h.y] == '*')
                continue;
            if (a[h.x][h.y] >= 'a' && a[h.x][h.y] <= 'j') //捡钥匙,状态要变
            {
                size = a[h.x][h.y] - 'a';    
                h.state = g.state | (1 << size);
            }
            if (((a[h.x][h.y] >= 'a' && a[h.x][h.y] <= 'z') || a[h.x][h.y] == '.' || 
                (a[h.x][h.y] >= 'A' && a[h.x][h.y] <= 'J' && (h.state & (1 << (a[h.x][h.y] - 'A'))) != 0)) //是钥匙 或者 路 或者 有对应钥匙能打开的门,但是一定要没去过
                && f[h.x][h.y][h.state] == -1)
            {
                f[h.x][h.y][h.state] = f[g.x][g.y][g.state] + 1;
                q.push(h);
            }
            if (a[h.x][h.y] == '^')
                if (f[g.x][g.y][g.state] + 1 < t)
                    return f[g.x][g.y][g.state] + 1;
                else
                    return -1;
        }
    }
    return -1;
}

int main()
{
    int i, j;
    while (cin >> n >> m >> t)
    {
        memset(f, -1, sizeof(f));
        while (q.size())
            q.pop();
        for (i = 0; i <= n + 1; i++)
            for (j = 0; j <= m + 1; j++)
                a[i][j] = '*';
        for (i = 1; i <= n; i++)
            for (j = 1; j <= m; j++)
            {
                cin >> a[i][j];
                if (a[i][j] == '@')
                {
                    sx = i;
                    sy = j;
                    a[i][j] = '.';
                }
            }
        cout << bfs() << endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值