zoukankan      html  css  js  c++  java
  • hdu 1429胜利大逃亡(续) (bfs+状态压缩)

    胜利大逃亡(续)

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


    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
     
    /*************************************************************************
        > File Name: code/hdu/1429.cpp
        > Author: 111qqz
        > Email: rkz2013@126.com 
        > Created Time: 2015年08月12日 星期三 15时56分05秒
     ************************************************************************/
    
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<string>
    #include<map>
    #include<set>
    #include<queue>
    #include<vector>
    #include<stack>
    #define y0 abc111qqz
    #define y1 hust111qqz
    #define yn hez111qqz
    #define j1 cute111qqz
    #define tm crazy111qqz
    #define lr dying111qqz
    using namespace std;
    #define REP(i, n) for (int i=0;i<int(n);++i)  
    typedef long long LL;
    typedef unsigned long long ULL;
    const int inf = 0x7fffffff;
    const int N=21;
    int n,m,t;
    int dx[4]={-1,1,0,0};
    int dy[4]={0,0,-1,1};
    int vis[N][N][1025];
    int ans;
    char maze[N][N];
    struct node
    {
        int x,y;
        int key;
        int time;
        bool ok()
        {
        if (x>=0&&x<n&&y>=0&&y<m&&maze[x][y]!='*')
            return true;
        return false;
        }
    }s;
    
    void bfs()
    {
        
        queue<node>Q;
        Q.push(s);
        while (!Q.empty())
        {
        node pre;
            pre = Q.front();Q.pop();
        if (maze[pre.x][pre.y]=='^')
        {
            ans = pre.time;
            return;
        }
        for ( int i = 0 ; i < 4 ; i++)
        {
            node next;
            next.x = pre.x + dx[i];
            next.y = pre.y + dy[i];
            next.time = pre.time + 1;
            next.key = pre.key;
            if (!next.ok()) continue;
            if (maze[next.x][next.y]>='a'&&maze[next.x][next.y]<='j')
            {
            int nkey = 1<<(maze[next.x][next.y]-'a'); //计算当前钥匙在钥匙串中的位置
            next.key = (next.key | nkey);          //将当前钥匙添加到钥匙串(状态压缩重点!)
            if (!vis[next.x][next.y][next.key])
            {
                vis[next.x][next.y][next.key]=true;
                Q.push(next);
            }
    
            }
            else
            if (maze[next.x][next.y]>='A'&&maze[next.x][next.y]<='J')
            {
            int needkey = (maze[next.x][next.y]-'A');// 计算当前门所需要的钥匙在钥匙串中的位置
            if (((next.key>>needkey)&1)&&!vis[next.x][next.y][next.key]) //next.key>>needkey 取出所需钥匙的状态 (状态压缩重点!)
            {
                vis[next.x][next.y][next.key] = true;
                Q.push(next); 
            }
            }
            else
            {
            if (!vis[next.x][next.y][next.key])
            {
                vis[next.x][next.y][next.key] = true;
                Q.push(next);
            }
            }
    
        }
    
        }
    
    }
    int main()
    {
        
        while (scanf("%d %d %d",&n,&m,&t)!=EOF)
        {
        memset(vis,false,sizeof(vis));
        for ( int i = 0 ; i < n ; i++ )
        {
            scanf("%s",maze[i]);
        }
        string kong;
        //    cin>>kong;
        for ( int i = 0 ; i < n ; i++ )
        {
            for ( int j = 0 ; j < m ; j ++ )
            {
            if (maze[i][j]=='@')
            {
                s.x = i;
                s.y = j;
            }
            }
        }
        s.key = 0;
        s.time  =0;
        vis[s.x][s.y][s.key]=true;
        ans = inf/2; //走不出可能是时间不够,也可能是时间够也走不出去(本身迷宫没有通路),因为忽略后一种情况wa了几次==
        bfs();
        if (ans<=t-1)
        {
            cout<<ans<<endl;
        }
        else
        {
            cout<<-1<<endl;
        }
        }
        return 0;
    }
    Source
     
    这道题不错.
    之前做过一个按照顺序找食物的,这道题和那个不一样.
    并不能用一般的dfs做掉,因为可能先遇到的门的钥匙在后拓展的路径才能遇到,这样标记了的话就回不来了.
    由于钥匙最多才10把,我们考虑状态压缩,一个十位二进制数表示当前拥有的"钥匙串"
    当遇到一个小写字母(钥匙)的时候,把这把钥匙添加到钥匙串中.比如我现在的钥匙串是0000001010,表示我有b,d两把钥匙这个时候我遇到了f
    那么我们先计算当前钥匙在钥匙串中所在的位置,构建一个只含这把钥匙的钥匙串  1<<('f'-'a') 为 0000100000,
    然后通过|(逻辑或)运算,把这个钥匙串添加到之前的钥匙串中 0000001010|0000100000 = 0000101010  表示我现在有了 b,d,f三把钥匙.
    对于遇到门的时候,我们要看这把门的钥匙在不在.做法是把这把门的钥匙所在的位置的状态取出来.
    比如我遇到了门D,当前的钥匙串是0000101010 , 0000101010>>(D-'A')=0000101 ,此时最后一位就是D门的状态
    0000101&1判断最后一位是否为1.
     
    其他就是一个朴素的bfs
    喵呜,第一次用结构体写.
    还有一点需要注意的是,无法逃脱可能有两种情况,一种是因为时间不够逃脱,另一种是迷宫本身没有通路导致不能逃脱,因为这个wa了好几次
    把 ans 初始设置成inf 就好.
    具体见代码
     
  • 相关阅读:
    Mybatis学习(2)原始dao开发和使用mapper接口代理开发
    Mybatis学习(1)
    Leetcode | Merge Intervals
    GDB打印STL容器内容
    LeetCode | Max Points on a Line
    最长不减子序列【转】
    LeetCode | Evaluate Reverse Polish Notation
    LeetCode | Word Ladder II
    LeetCode | Valid Number
    LeetCode | Set Matrix Zeroes
  • 原文地址:https://www.cnblogs.com/111qqz/p/4728418.html
Copyright © 2011-2022 走看看