首页 > 其他分享 >[刷题笔记] Luogu P2895 Meteor Shower S

[刷题笔记] Luogu P2895 Meteor Shower S

时间:2023-06-04 11:45:07浏览次数:64  
标签:Node && int Luogu Shower vis Meteor include 流星

Problem

Solution

显然bfs,只不过有了限定条件,有实时的流星雨

这里提供两种做法:

Solution 1

这也是我一开始的做法

模拟实时流星,由于bfs是按层搜的,是严格按照时间递增搜的,故可以模拟实时放流星。
需要注意放流星的时间,如果第\(t\)秒有流星,则该秒不可以走,需要在每一秒前放流星。

那么如何判定该点安全呢?
可以开两个数组,一个存实时流星,另一个存是否有流星,具体见代码

实现的时候还需要注意Bessie可以走出300,300只是流星的界限。

Code 1:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#define INF 0x3f3f3f3f
#define N 1010
#define M 500010
using namespace std;
int m;
int mapp_boom[N][N];
int mapp[N][N];
int vis[N][N];
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
struct Node
{
    int x,y,t;
}rock[M];
bool get_ans = false;
bool check_add(int x,int y) 
{
    return x >= 0 && x <= 390 && y >= 0 && y <= 390; //开大界限
}
void boom(int t)
{
    for(int i=1;i<=m;i++) 
    {
        if(t == rock[i].t) 
        {
            mapp[rock[i].x][rock[i].y] = 1;
            for(int j=0;j<4;j++)
            {
                int ax = rock[i].x + dx[j];
                int ay = rock[i].y + dy[j];
                if(check_add(ax,ay)) mapp[ax][ay] = 1; //实时放流星
            }
        }
    }
}
void bfs()
{
    queue <Node> q;
    q.push(Node{0,0,0});
    vis[0][0] = 0;
    while(!q.empty())
    {
        Node p = q.front();
        q.pop();
        boom(vis[p.x][p.y]+1);
        if(!mapp_boom[p.x][p.y]) //如果该点安全
        {
            cout<<vis[p.x][p.y]<<endl;
            get_ans = true;
            return;
        }
        for(int i=0;i<4;i++)
        {
            int ax = p.x + dx[i];
            int ay = p.y + dy[i];
            if(check_add(ax,ay) && !mapp[ax][ay] && vis[ax][ay] == INF) 
            {
                vis[ax][ay] = vis[p.x][p.y] + 1;
                q.push(Node{ax,ay,0});
             //   boom(vis[ax][ay]);
            }
        }
    }
}
int main()
{
    scanf("%d",&m);
    memset(vis,INF,sizeof(vis));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&rock[i].x,&rock[i].y,&rock[i].t);
        mapp_boom[rock[i].x][rock[i].y] = 1;
        for(int j=0;j<4;j++) 
        {
            int ax = rock[i].x + dx[j];
            int ay = rock[i].y + dy[j];
            if(check_add(ax,ay)) mapp_boom[ax][ay] = 1; //标记该点危险
        }
    }
    boom(0); //需要注意放流行要在该秒前放
    bfs();
    if(!get_ans) printf("-1\n");
    return 0;
}

Solution 2

教练提供的思路,相较于Sol 1更加干净利落一点

我们可以开一个数组记录该点被炸的时间(预处理),然后普通bfs即可

预处理的时候如果该流星炸这个点的时间比原来流星炸这个点的时间早就可以覆盖。

需要注意第0秒可能有流星,所以没有流星不能用0表示。被卡了ww

相较于Sol 1,只需要一个数组就可以完成流星时间以及是否有流星两个操作,非常简洁。

Code 2:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#define N 1010
#define INF 0x3f3f3f3f
using namespace std;
bool get_ans = false;
typedef pair<int,int> PAIR;
int m;
int boom[N][N];
int vis[N][N];
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
struct Node
{
    int x,y,t;
}b[500000];
bool cmp(Node a,Node b)
{
    return a.t < b.t;
}
void bfs()
{
    queue <PAIR> q;
    q.push(PAIR(0,0));
    vis[0][0] = 0;
    while(!q.empty())
    {
        PAIR p = q.front();
        q.pop();
        if(boom[p.first][p.second] == -1) 
        {
            cout<<vis[p.first][p.second]<<endl;
            get_ans = true;
            return;
        }
        for(int i=0;i<4;i++)
        {
            int ax = p.first + dx[i];
            int ay = p.second + dy[i];
            if(ax>=0&&ax<=400&&ay>=0&&ay<=400&&vis[ax][ay] == INF&&(vis[p.first][p.second]+1 < boom[ax][ay] || boom[ax][ay] == -1))
            {
                vis[ax][ay] = vis[p.first][p.second] + 1;
                q.push(PAIR(ax,ay));
            }
        }
    }
}
int main()
{
    memset(boom,-1,sizeof(boom));//不得使用0
    memset(vis,INF,sizeof(vis));
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].t); 
    }
    for(int i=1;i<=m;i++)
    {
        if(boom[b[i].x][b[i].y] == -1 || b[i].t < boom[b[i].x][b[i].y])
            boom[b[i].x][b[i].y] = b[i].t;
        for(int j=0;j<4;j++) 
        {
            int ax = b[i].x+dx[j];
            int ay = b[i].y+dy[j];
            if(ax>=0&&ax<=400&&ay>=0&&ay<=400&&(boom[ax][ay] == -1 || b[i].t < boom[ax][ay])) boom[ax][ay] = b[i].t;
        }
    }
    bfs();
    if(!get_ans) cout<<"-1"<<endl;
    return 0;
}

Double Exp
和流星几乎一样,也可以使用这两种实现方法,只是路障砸下的时间可以走而已.

标签:Node,&&,int,Luogu,Shower,vis,Meteor,include,流星
From: https://www.cnblogs.com/SXqwq/p/17455418.html

相关文章

  • [刷题笔记] LuoguP2658 汽车拉力比赛
    ProblemSolution需要找到最小满足题意的\(d\),显然\(d\)满足单调性,考虑二分二分\(d\),然后直接bfs,每次bfs判断能不能走的时候还需要加上高度差不超过二分的\(d\)(即满足),bfs跑完后看看所有的路标都被访问了没。(可以记录个数,因为不可能重复走)二分的时候注意\(l\)从0开始,不然会WA......
  • luogu P8497 [NOI2022] 移除石子
    题面传送门不好评价?首先我们考虑最基础的情况,当\(k=0,l_i=r_i\)时,相当于我们需要判定一个状态能不能被消完。这相当于我们要执行若干次\(2\)操作,使得每个位置要么大于等于\(2\),要么为\(0\)。为此我们需要挖掘一些操作\(2\)的性质。性质\(1\):操作区间长度不会超过\(5......
  • Luogu P1007 独木桥
    题目描述link思路找到独木桥的中间位置,最少时间考虑在端点左侧的,向左走,在端点右侧的向右走.最多时间考虑在端点左侧的向右走,在端点右侧的向左走.最少时间即为最优情况下最多的时间,最多时间即为最差情况下的最多时间Code#include<cstdio>#include<algorithm>......
  • Luogu P1008 三连击
    题目描述link思路因为\(1-9\)且不能重复使用,所以从\(123\)循环至\(789\),相应的\(2\)倍,\(3\)倍,即为另两个数字.对每个数字进行拆分,所用数字使用次数\(+1\),判断是否每个数字都被使用且只使用一次,输出即可.Code#include<cstdio>#include<cstring>i......
  • 最小生成树_LuoguP1669
    P1669P1669[USACO04DEC]BadCowtractorsS题目传送门题意简化:在一个有\(N\)个点\(M\)条边的图中选出\(N-1\)条边构成一棵树,使得树的总边权最大,求最大总边权。上述问题即为最小(大)生成树问题,本题为最大生成树,如有未详者可以移步P3366。该问题一般是Kruskal和Prim......
  • Luogu P1903 [国家集训队] 数颜色 / 维护队列
    题目来源https://www.luogu.com.cn/problem/P1903[国家集训队]数颜色/维护队列题目描述墨墨购买了一套\(N\)支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:\(Q\L\R\)代表询问你从第\(L\)支画笔到第\(R\)支画笔中共有几种......
  • 刷题笔记:Luogu P3956 棋盘
    ProblemSolutionDFS/BFS需要注意去重的时候可以重复走(因为有限定条件),只要新的步数比原来的步数小就可以走,其余情况模拟即可细节有点多,比如需要记录一下上一步的棋盘颜色(下一次搜索传递参数),因为牵扯到使用魔法问题,不能直接染,因为改变地图后后边很多操作都会受影响在列举可能性......
  • Luogu P2801 教主的魔法(Loj 数列分块入门 2)
    教主的魔法题目描述教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是\(N\)个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为\(1,2,\ldots,N\)。每个人的身高一开始都是不超过\(1000\)的正整数。教主的魔法每次可以把闭......
  • Luogu P5643 [PKUWC2018]随机游走
    题意给出一棵\(n\)结点树,从结点\(x\)出发,每次从当前点的所有边中选一条走过去,\(Q\)次询问给定一个点集\(S\),随机游走直到经过\(S\)中的每一个点至少一次的期望总步数,出发点\(x\)默认在开始时已经被经过。\(n\le18,Q\le5000\)解法萌新第一次见到这种题,感觉很神。......
  • Luogu P3978 [TJOI2015] 概率论
    定义\(f_i\)为\(i\)个节点组成的二叉树数量,\(g_i\)为\(i\)个节点组成的二叉树的叶子节点个数之和设当前\(i\)个节点组成的二叉树有\(a\)个叶子,容易发现分别删掉其中的\(1\)个叶子节点就能得到一个对应的\(i-1\)个节点的二叉树,总共会有\(a\)颗,可以发现每一个叶......