洛谷题单算法【1-7】搜索:流星雨

题目描述

贝茜听说了一个骇人听闻的消息:一场流星雨即将袭击整个农场,由于流星体积过大,它们无法在撞击到地面前燃烧殆尽,届时将会对它撞到的一切东西造成毁灭性的打击。很自然地,贝茜开始担心自己的安全问题。

以 Farmer John 牧场中最聪明的奶牛的名誉起誓,她一定要在被流星砸到前,到达一个安全的地方(也就是说,一块不会被任何流星砸到的土地)。

如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。

根据预报,一共有 M M M 颗流星 ( 1 ≤ M ≤ 50 , 000 ) (1≤M≤50,000) (1M50,000) 会坠落在农场上,其中第 i i i颗流星会在时刻 T i T_i Ti ( 0 ≤ T i ≤ 1 , 000 ) (0\leq T_i\leq1,000) (0Ti1,000)砸在坐标为 ( X i , Y i ) (X_i, Y_i) (Xi,Yi) ( 0 ≤ X i ≤ 300 , 0 ≤ Y i ≤ 300 ) (0\leq X_i\leq 300,0\leq Y_i\leq300) (0Xi300,0Yi300)的格子里。

流星的力量会将它所在的格子,以及周围 4 4 4 个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。

贝茜在时刻 0 0 0 开始行动,它只能在第一象限中,平行于坐标轴行动,每 1 1 1 个时刻中,她能移动到相邻的(一般是 4 4 4 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 t t t 被流星撞击或烧焦,那么贝茜只能在 t t t 之前的时刻在这个格子里出现。 贝西一开始在 ( 0 , 0 ) (0,0) (0,0)

请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 − 1 -1 1

输入格式

1 1 1 1 1 1个整数 M M M
2 2 2到第 M + 1 M+1 M+1行,每行3个整数,分别为 X i X_i Xi Y i Y_i Yi T i T_i Ti,用空格分隔。

输出格式

一个整数 t t t,表示贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 − 1 -1 1

输入样例

4
0 0 2
2 1 2
1 1 2
0 3 5

输出样例

5

算法思想(BFS)

这是一个与时间赛跑的游戏,需要计算的是贝茜最少需要多少时间才能到达一个安全的格子。

根据输入能得到每个格子被流星击中的时间,如果再能计算出贝茜移动到每个格子的最短时间,就能判断出贝茜能否安全地到达该格子,那么贝茜移动到第一个安全(没有被流星击中或者波及到)的格子的时间就是最终答案。

因此可以考虑使用广度优先搜索,记录到达每个格子的最短时间。

算法实现

  • 输入每颗流星击中的坐标 ( x , y ) (x, y) (x,y)和时间 t t t,得到 ( x , y ) (x,y) (x,y)以及周围4个格子被陨石击中的最小时刻。

广度优先搜索到达每个格子的短时间:

  • 将起点 ( 0 , 0 ) (0,0) (0,0)加入队列
  • 只要队列不空:
    • 从队列取出一点
    • 如果该点是安全的,那么返回到达该点的最短时间
    • 否则从该点进行扩展,如果在到达该点之前流星还没有落下,则将该点加入队列,继续搜索
  • 搜索结束还没有返回,则意味着无法到达安全格子,返回 − 1 -1 1

代码实现

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 310, M = 1010, INF = 0x3f3f3f3f;
//mt[x][y]表示陨石集中(x,y)的最小时刻
int mt[N][N];
//dis[x][y]表示走到(x,y)的最短时间
//st[x][y]标记(x,y)是否访问过
int dis[N][N], st[N][N]; 
int dx[] = {0, -1, 0, 1, 0}, dy[] = {0, 0, 1, 0, -1};
int bfs()
{
    int x = 0, y = 0;
    queue<PII> q;
    dis[x][y] = 0, st[x][y] = 1; 
    q.push({x, y});
    while(q.size())
    {
        PII t = q.front(); q.pop();
        x = t.first, y = t.second;
        //该点是安全的
        if(mt[x][y] == INF) return dis[x][y];
        
        for(int i = 0; i < 5; i ++)
        {
            int a = x + dx[i], b = y + dy[i];
            if(a < 0 || a >= N || b < 0 || b >= N) continue;
            if(st[a][b]) continue;
            dis[a][b] = dis[x][y] + 1;
            //陨石击中(a,b)时间<=走到(a,b)的时间
            if(mt[a][b] <= dis[a][b]) continue;
            st[a][b] = 1;
            q.push({a, b});
        }
    }
    return -1;
}

int main()
{
    int n;
    scanf("%d", &n);
    //初始化为无穷大
    memset(mt, 0x3f, sizeof mt);
    for(int i = 0; i < n; i ++)
    {
        int t, x, y;
        scanf("%d%d%d", &x, &y, &t);
        for(int j = 0; j < 5; j ++)
        {
            int a = x + dx[j], b = y + dy[j];
            if(a < 0 || a >= N || b < 0 || b >= N) continue;
            mt[a][b] = min(mt[a][b], t); //保存陨石撞击的最小时刻
        }
    }
    printf("%d\n", bfs());
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值