POJ 3669-Meteor Shower [bfs] 《挑战程序设计竞赛》2.1

题目链接: POJ 3669 Meteor Shower

题目大意:

你在坐标原点, 只能在第一象限沿着x,y轴方向移动, 单位时间内一次移动一格。马上将要迎来一场流星雨, 也只会落到第一象限, 每颗流行砸下来, 会毁掉它降落的位置以及直接相邻的上下左右四个位置。在某一块地区被流星摧毁后, 你不能再来这个地方。
一共有M颗流星, 每颗的坐标 Xi,Yi 以及降落时间 Ti 已知。
求你能到达安全地带的最短时间

题解:

求到达安全地带所需的最短时间, 容易想到用bfs。和走迷宫找最短路径不同的是, 这里的地图是随着时间变化的,因为不同流星落降落时间是不同的。
我的解法:

首先,根据所有的流星的降落位置, 计算出最后状态的地图,记作 safe[][] , 这样就知道哪里是安全的, 哪里是不安全的。
先按照时间顺序给流星排个序,
然后, 每次宽搜,要在宽搜节点中加入一个时间属性。如果时间状态为 i 的节点都已经宽搜结束, 则需要更新地图的时间状态为 i+1 , 然后在进行 i+1 的时间状态的搜索。
我用了一个 num[i] 数组来存放时间状态为 i <script type="math/tex" id="MathJax-Element-144">i</script> 的节点的个数。 num[i] 为0 时,则更新地图。

用自己的方法AC之后, 找了别人的题解, 发现其实并不需要这样随着时间来更新地图, 可以用地图记录每个地方第一次被摧毁的时间。这样地图本身就有了时间属性。

代码:

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define MAXM 50010
#define MAXX 310
using namespace std;

struct Meteor {
    int x, y, t;
}m[MAXM];
int M, ans;
int safe[MAXX][MAXX];
int map[MAXX][MAXX];
bool f[MAXX][MAXX];
int num[MAXM];
int fx[] = {0, 1, 0, -1};
int fy[] = {1, 0, -1, 0};
queue<Meteor> q;

bool cmp(const Meteor &a, const Meteor &b) {
    return a.t < b.t;
}

//摧毁
void boom(int (*a)[MAXX], int x, int y) {
    a[x][y] = 1;
    a[x-1][y] = 1;
    a[x][y-1] = 1;
    a[x+1][y] = 1;
    a[x][y+1] = 1;
}

bool is_safe(int x, int y) {
    if (x > 0 && y > 0 && !safe[x][y]) return true;
    else return false;
}
int bfs() {
    memset(f, false, sizeof(f));
    memset(num, 0, sizeof(num));
    Meteor o = {1, 1, 0};
    q.push(o);
    int cnt = 0;
    int p = 0;
    f[1][1] = true;
    num[0] = 1;
    //更新0时间的地图
    for (; m[p].t == cnt; p++) boom(map, m[p].x, m[p].y);
    cnt++;
    //更新1时间的地图
    for (; m[p].t == cnt; p++) boom(map, m[p].x, m[p].y);
    cnt++;
    while (!q.empty()) {
        Meteor a = q.front();
        q.pop();
        int x, y, t;
        x = a.x; y = a.y; t = a.t;

        if (is_safe(x, y)) {
            return t;
        }

        for (int i = 0; i < 4; i++) {
            int tx = x + fx[i];
            int ty = y + fy[i];
            if (0 < tx && 0 < ty && !map[tx][ty] && !f[tx][ty]) {

                f[tx][ty] = true;
                Meteor tmp = {tx, ty, t+1};
                q.push(tmp);
                num[t+1]++;
            }
        }
        num[t]--;
        if (!num[t]) {
            //更新cnt时间的地图
            for (; p < M && (m[p].t == cnt); p++) {
                boom(map, m[p].x, m[p].y);
            }

            cnt++;
        }
    }
    return -1;
}

int main() {
    memset(safe, 0, sizeof(safe));
    memset(map, 0, sizeof(map));
    scanf("%d", &M);
    for (int i = 0; i < M; i++) {
        scanf("%d%d%d", &m[i].x, &m[i].y, &m[i].t);
        m[i].x++; m[i].y++;
        boom(safe, m[i].x, m[i].y);
    }
    sort(m, m+M, cmp);
    ans = bfs();
    printf("%d\n", ans);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值