HDU 1885 Key Task(状态压缩+BFS)

这篇博客介绍了如何使用状态压缩和BFS解决一个包含钥匙和门的矩阵迷宫问题。作者强调理解何时标记状态以及如何表示手中钥匙的情况,通过一个压缩数组来简化表示。在解决过程中,作者遇到并解决了因忘记在外层循环中加入break而导致的错误。虽然数据规模不大,但作者最初尝试使用map进行标记,发现结构体作为key需要重载操作符,因此改用三维数组实现。博客提醒读者,拿完钥匙后可以重新走过已访问的格子,这是允许的策略。

这道题和UVA上面的一道题目很类似,需要钥匙的题目,那道题是开关,也差不多。不同的是,那道题不需要走到需要关灯的房间就能控制,而这个需要走到门的位置。还有就是房间的表示是不一样的,那个是一维的这个是二维。

但是思想都是一样

题目:矩阵,有的地方有钥匙,有的地方有门,有的是墙,有的是起点还有终点,还有路。门必须用相应的钥匙可以打开。出口有多个,这个要注意。

第一次wa就是wa在这里了,因为,在break一次后,外层循环也需要一个break,可以小妞我给忘了!!!

好吧,说一下分析:主要有一点药想明白,标记什么,状态怎么表示!状态这里,无非就是设计了两个东西,一个是位置,另一个是手里的钥匙的情况。可以用一个数组表示手里钥匙的情况,但是为了便于标记,把这个钥匙的情况压缩一下,就妥妥哒了!

数据不大,开始我想用的是map来标记,可惜如果key是结构体的话,那么需要用重载的,我就是傻呀,不会先试一下呀,这下可好,写了一大篇子代码,编译不过,这我就不高兴了,于是果断重写,用三维数组来表示,好在数据不大。下面就来表示一下重载的怎么写,学习的链接:http://bbs.chinaunix.net/thread-1538318-1-1.html

接着来说,千万不要标记已经走过的格子,因为拿完钥匙可以再走,这是允许的

好了,千言万语,尽在代码之中:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cctype>
#include <map>
using namespace std;

const int N = 101;
const int INF = 100000000;
int n, m, ans, d[N][N];
int sx, sy, ex, ey;
char g[N][N];
bool vis[N][N][20];
vector <pair<int, int> > v;
struct Node{ int x, y, h; }e;
int match( char x ) {
    if ( x == 'B' || x == 'b' ) return 0;
    if ( x == 'Y' || x == 'y' ) return 1;
    if ( x == 'R' || x == 'r' ) return 2;
    if ( x == 'G' || x == 'g' ) return 3;
}
bool BFS() {
    bool find = false;
    queue< Node > Q;
    e.x = sx, e.y = sy, e.h = 0;
    Q.push(e);
    memset(vis, 0, sizeof(vis) );
    vis[sx][sy][0] = true;
    int x[4] = { 0, 0, 1, -1 }, y[4] = { 1, -1, 0, 0 };
    memset( d, 0, sizeof(d) );
    //for ( int i = 0; i < v.size(); ++i ) d[v[i].first][v[i].second] = INF;
    while ( !Q.empty() ) {
        Node u = Q.front(); Q.pop();
      //printf("%d %d %d %d\n", u.x, u.y, u.h, d[u.x][u.y]);
        for ( int i = 0, xx, yy, move; i < 4; ++i ) {
            xx = u.x + x[i], yy = u.y + y[i], move = match( g[xx][yy] ), e.x = xx, e.y = yy, e.h = u.h;
            if ( g[xx][yy] == '#' ) continue;
            if ( g[xx][yy] == '.' || g[xx][yy] == '*' ) {
                if ( !vis[e.x][e.y][e.h] ) {
                    vis[e.x][e.y][e.h] = true;
                    Q.push(e);
                    d[xx][yy] = d[u.x][u.y] + 1;
                }
            }
            else if ( g[xx][yy] == 'X' ) {
                find = true;
                ex = xx, ey = yy;
                d[ex][ey] = d[u.x][u.y] + 1;
              //printf("--%d %d\n", ex, ey);
                break;
            }
            else if ( islower(g[xx][yy]) ) {
                e.h = (u.h | ( 1 << move ));
             //printf("%d %d %d %d\n",e.x, e.y, e.h, d[e.x][e.y]);
                if ( !vis[e.x][e.y][e.h] ) {
                    vis[e.x][e.y][e.h] = true;
                    Q.push(e);
                    d[xx][yy] = d[u.x][u.y] + 1;
                }
            }
            else if ( isupper(g[xx][yy]) ) {
                if ( (u.h & ( 1 << move ) )== 0 ) continue;
                e.h = u.h;
                if ( !vis[e.x][e.y][e.h] ) {
                    vis[e.x][e.y][e.h] = true;
                    Q.push(e);
                    d[xx][yy] = d[u.x][u.y] + 1;
                }
            }
        }
        if ( find ) break;
    }
    return find;
}
    
int main()
{
    while ( scanf("%d%d", &n, &m) != EOF && ( n || m ) ) {
        getchar();
        ans = INF;
        memset( g, '\0', sizeof(g) );
        for ( int i = 0; i <= n+1; ++i ) g[i][0] = g[i][m+1] = '#';
        for ( int i = 0; i <= m+1; ++i ) g[0][i] = g[n+1][i] = '#';
        for ( int i = 1; i <= n; ++i, getchar() )
            for ( int j = 1; j <= m; ++j ) {
                scanf("%c", &g[i][j]);
                if ( g[i][j] == '*' ) sx = i, sy = j;
                /*else if ( g[i][j] == 'X' ) {
                    pair<int, int> p;
                    p.first = i, p.second = j;
                    v.push_back( p );
                }*/
            }
        //for ( int i = 0; i <= n+1; ++i ) printf("%s\n", g[i]);
        if ( BFS() ) printf("Escape possible in %d steps.\n", d[ex][ey]);
        else printf("The poor student is trapped!\n");
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值