zoj3652 Maze

题意:给定一个n * m  的矩阵(迷宫),-1 表示该位置不能通过, 1 ~ 5 表示该位置被第i个怪控制, 0 表示该位置不被任何怪控制
给定起点和终点, 问Celica 最少需要多少回合才能通过。当然,还有很多限制条件:
1)每一个回合, Celica  的初始行动值为L, 没移动一个位置,行动值-1, 当行动值减为0, 则开始下一个回合
2)一旦踏入怪的所在位置,则立即会将怪杀死,怪所控制的位置也就变为0了
3)当移动到一个怪所控制的位置,行动立即减为0
4)每到达一个位置,首先变化的是行动值, 这就意味着,如果踏入一个怪所在的位置,而且该位置是被怪所控制的情况下,那么行动值先变为0,再将怪杀死
5)怪不一定在它控制的位置
6)起点一定没有怪, 但可能被某一个怪控制
 
分析:除了弄清题意外,首先应该注意到的是数据范围, n <= 50, 这种地图略大, 且求的是最小值,一般用的是广搜。 当然,如果地图再小一点,也可能是用IDA*等搜索方法解决。
不过,我首先想到的是用IDA, 通过限制回合数,深搜来求得最小回合数。不过没想到更好的剪枝方法。 为什么没想到广搜呢?其实是因为每移动一步,地图可能改变,那每一个状态都要将整个地图保存么?这个问题一开始没
想通, 所以就没敲了。 后来突然想到,地图改变是因为可能是某些怪挂掉了,而且最多才五只怪。那么只要知道五只怪的存活情况,就知道当前的地图是怎么样的了。
另外要考虑的问题就是,状态的表示了,表示一个唯一的状态。对于这种迷宫问题,首先就是考虑一个点是否只能走一次? 不是的,试想一下,是否存在一种情况,经过某一个点去杀一个怪,杀玩完再经过这个点去终点却存在最优值呢?
答案是肯定的。所以,我们应该用vis[x][y][state] 标记经过该点时的状态, state 表示 五只怪的存活情况,如果第二次经过该点时state的值没变, 那么也就没必要将该点重新压入队列, 当然,这里说的是优先队列, 因为每一次的
代价并非相等的
 
zoj3652
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
#include<queue>

using namespace std;

const int N = 50 + 10;

int g[N][N];

struct node
{
    int t, mob, state;//t 表示回合数, mob 表示当前回合剩余行动值
    int x, y;
    node(){}
    node(int x, int y, int t, int mob, int state):x(x), y(y), t(t), mob(mob), state(state){}
    bool friend operator < (const node &a, const node &b)
    {
        if(a.t != b.t) return a.t > b.t;
        return a.mob < b.mob;
    }
};

priority_queue<node> Q;

int n, m, L;
bool vis[N][N][32];
int mark[N][N];//标记该位置存在哪一个怪,-1表示不存在怪
int si, sj, ei, ej;
int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

int BFS()
{
    while(!Q.empty())
        Q.pop();
    memset(vis, false, sizeof(vis));
    vis[si][sj][0] = true;
    Q.push(node(si, sj, 0, L, 0));
    while(!Q.empty())
    {
        node cur = Q.top();
        Q.pop();
        //cout << cur.x << ' ' << cur.y << ' ' << cur.t << ' ' << cur.mob << ' ' << cur.state << endl;
        if(cur.x == ei && cur.y == ej) 
        {
            if(cur.mob == L)
                return cur.t;
            return cur.t + 1;
        }
        for(int k = 0; k < 4; ++k)
        {
            int x = cur.x + dir[k][0], y = cur.y + dir[k][1];
            if( x <= 0 || x > n || y <= 0 || y > m || g[x][y] == -1) continue;
            int mob = cur.mob - 1, state = cur.state;
            if(g[x][y] != 0)
            {
                int t = g[x][y];
                if(!(cur.state & (1 << (t - 1))))//判断怪是否已经被杀死
                    mob = 0;
            }    
            if(mark[x][y] >= 0) //怪所在的位置,则杀了怪,改变状态
                state |= (1 << mark[x][y]);

            if(vis[x][y][state]) continue;
            int turn = cur.t;
            if(mob == 0)
            {
                mob = L;
                turn++;
            }
            vis[x][y][state] = true;
            Q.push(node(x, y, turn, mob, state));
        }
    }
    return -1;
}
int main()
{
    int x, y;
    while(scanf("%d %d %d",&n, &m, &L) == 3)
    {
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j)
                scanf("%d",&g[i][j]);
        memset(mark, -1, sizeof(mark));
        int k;
        scanf("%d", &k);
        for(int i = 0; i < k; ++i)
        {
            scanf("%d %d",&x, &y);
            mark[x][y] = i;
        }
        scanf("%d %d %d %d",&si, &sj, &ei, &ej);
        int ans = BFS();
        if(ans == -1) puts("We need God's help!");
        else
        printf("%d\n", BFS());
    }
    return 0;
}

 

posted @ 2013-04-10 11:15  枕边梦  阅读(366)  评论(0编辑  收藏  举报