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;
    }
    
  • 相关阅读:
    委托的说明和举例
    用C#编写获取远程IP,MAC的方法
    200个Gmail邀请,要的请留下邮箱地址
    .NET中各种数据库连接大全
    .net中何有效的使用Cache
    55种网页常用小技巧(javascript) (转)
    一个WEB项目安装包,自动配置数据库,config文件和虚拟目录。。(转)
    windows xp sp2后所有更新
    C#反射实例(转)
    可扩展的应用程序:新增功能时无须重新编译
  • 原文地址:https://www.cnblogs.com/littlehb/p/15064252.html
Copyright © 2011-2022 走看看