zoukankan      html  css  js  c++  java
  • P2895 [USACO08FEB]Meteor Shower S 题解

    题目传送门

    感悟与理解

    1、需要上下左右进行移动的,需要使用delta变量数组,这玩意有四个方向的,也有八个方向的,还有马走日之类的,总之,一事一议吧。

    //二维坐标系中每个点的上下左右变化delta坐标
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {1, -1, 0, 0};
    

    2、有多个方向,就需要判断是否有数组范围内,是不是出界,包括左出界,右出界,上出界来下出界。

     if (nx >= 0 && ny >= 0) //本题是第一象限,所以只需要检查是不是大于等于0即可,上和右可以理解为无限制。当然,数组认为足够大就是无限制,本题目数据范围310。
    

    3、结构体
    一般向bfs队列中添加的,基本上没有原始的int型数据,一般是结构体,也很好理解,因为结构体才能保存更多信息吗,比如x,y坐标,还是到这走了几步了,就是step.

    //定义结构体,x,y这个坐标
    struct coord {
        int x, y;
    };
    

    4、访问过的标识+从原点出发需要走几步到达这个位置

    int st[N][N];          //访问过的标识+从原点出发需要走几步到达这个位置
    ...
    //标识走过了0步,到达了这个位置
    st[0][0] = 0;
    ...
    st[nx][ny] = st[u.x][u.y] + 1;
    

    5、预处理(关键)
    为什么要预处理呢?预处理些什么东西呢?
    (1) 题目中说到,每个陨石,它的破坏范围是5个(也可能在边界处小于5个),就是坐标位置一个,四周四个,共5个。如果我们只记录了陨石的落点,那肯定是不够的,因为周边的四个信息丢失了,所以我们需要一个二维数组记录每个点的信息,行和列无疑是代表坐标的xy了,那么值代表什么含义呢?就是最早砸中时间。同时,由于一个点可能是被N块陨石关联到,比如它自己被砸一次,过一会又被旁边点带上一次,就是多次嘛,我们记录最早的关联时间就可以了。为什么呢?因为一旦被标识为破坏状态,我们后面就走不了了,对我们就没有意义了,我才不管它后面还被砸几次呢。

     //预处理的过程
            //Q:预处理的是什么?
            //A:每个点,都有一个最早的破坏时间,可能是直接被砸中,也可能是被牵连~,那么,这个点,
            // 是什么时间被一次覆盖到呢?需要预处理出来
            //求出该点和周围点的最早摧毁时间
            a[x][y] = min(a[x][y], t); //如果直接砸中的时间小于原来记录的时间,需要修改一下。
            for (int j = 0; j < 4; j++) {
                //可以到达的新坐标位置
                int nx = x + dx[j];
                int ny = y + dy[j];
                //一直在第一象限行动,只要nx,ny不小于0,可以随意向右,向上去走,右和上没有边界
                if (nx >= 0 && ny >= 0) a[nx][ny] = min(a[nx][ny], t);
            }
    

    6、怎么样知道哪个点是永远安全的?
    如果一个点,在预处理后,还是没有被关联到,就是安全的,奶牛可以一直呆在这里~

    //如果这个点为INF,表示永远不会被摧毁,已经安全了
    if (a[nx][ny] == INF) return st[nx][ny] = st[u.x][u.y] + 1;
    

    7、怎么样知道某个点在某个时间是否可以走呢?
    (1)这个点没有走过。
    (2)到达时间小于最早摧毁时间

    //如果到达这个时间点的时间早于最早摧毁时间,可以走
    if (st[u.x][u.y] + 1 < a[nx][ny] && st[nx][ny] == -1) {
                    st[nx][ny] = st[u.x][u.y] + 1;
                    q.push({nx, ny});
                }
    

    8、C++代码

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int N = 310;
    
    //二维坐标系中每个点的上下左右变化delta坐标
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {1, -1, 0, 0};
    
    int a[N][N];           //x,y坐标的最早被流星覆盖到的时间
    int st[N][N];          //访问过的标识+从原点出发需要走几步到达这个位置
    
    //定义结构体,x,y这个坐标,到达需要step步骤
    struct coord {
        int x, y;
    };
    
    //广度优先搜索
    int bfs() {
        queue<coord> q;
    
        //出发的原点位置
        q.push({0, 0});
    
        //标识走过了0步,到达了这个位置
        st[0][0] = 0;
    
        //广度优先搜索的框架
        while (!q.empty()) {
            //队列首
            coord u = q.front();
            //出队列
            q.pop();
            //四个方向
            for (int i = 0; i < 4; i++) {
                int nx = u.x + dx[i];
                int ny = u.y + dy[i];
                //出了第一象限是不可以的
                if (nx < 0 || ny < 0)continue;
    
                //如果这个点为INF,表示永远不会被摧毁,已经安全了
                if (a[nx][ny] == INF) return st[nx][ny] = st[u.x][u.y] + 1;
    
                //如果到达这个时间点的时间早于最早摧毁时间,可以走
                if (st[u.x][u.y] + 1 < a[nx][ny] && st[nx][ny] == -1) {
                    st[nx][ny] = st[u.x][u.y] + 1;
                    q.push({nx, ny});
                }
            }
        }
        return -1;
    }
    
    int main() {
        //初始化为INF(预求最小,先设最大)
        memset(a, 0x3f, sizeof a);
    
        //初始化为-1,表示没有走过
        memset(st, -1, sizeof st);
    
        //一共m个流星
        int m;
        cin >> m;
        for (int i = 1; i <= m; i++) {
            int x, y, t;
            cin >> x >> y >> t;
    
            //预处理的过程
            //Q:预处理的是什么?
            //A:每个点,都有一个最早的破坏时间,可能是直接被砸中,也可能是被牵连~,那么,这个点,
            // 是什么时间被一次覆盖到呢?需要预处理出来
            //求出该点和周围点的最早摧毁时间
            a[x][y] = min(a[x][y], t); //如果直接砸中的时间小于原来记录的时间,需要修改一下。
            for (int j = 0; j < 4; j++) {
                //可以到达的新坐标位置
                int nx = x + dx[j];
                int ny = y + dy[j];
                //一直在第一象限行动,只要nx,ny不小于0,可以随意向右,向上去走,右和上没有边界
                if (nx >= 0 && ny >= 0) a[nx][ny] = min(a[nx][ny], t);
            }
        }
        //广度优先搜索
        printf("%d
    ", bfs());
        return 0;
    }
    
  • 相关阅读:
    poj 2187 Beauty Contest(旋转卡壳)
    poj 2540 Hotter Colder(极角计算半平面交)
    poj 1279 Art Gallery(利用极角计算半平面交)
    poj 3384 Feng Shui(半平面交的联机算法)
    poj 1151 Atlantis(矩形面积并)
    zoj 1659 Mobile Phone Coverage(矩形面积并)
    uva 10213 How Many Pieces of Land (欧拉公式计算多面体)
    uva 190 Circle Through Three Points(三点求外心)
    zoj 1280 Intersecting Lines(两直线交点)
    poj 1041 John's trip(欧拉回路)
  • 原文地址:https://www.cnblogs.com/littlehb/p/15064252.html
Copyright © 2011-2022 走看看