• P2895 [USACO08FEB]Meteor Shower S 广度搜索 剪枝


    P2895 [USACO08FEB]Meteor Shower S

    使用标记来剪枝,多几行代码少算一年(虚指).

    没想到bfs也有今天.(你也得剪枝了)

    此外,还有一个预处理的方法.

    最初想法就是很常规的bfs,不过有很多要判断的条件,写起来比较麻烦,但是起码能写出来.细节见注释.

    需要开一个存储结构体的队列,每次push的参数包含一个坐标和移动到该坐标的时刻.

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int INF = 100000000;
    
    int n, s[310][310], ans = -1;        // burn time
    int dx[4] = {-1, 0, 0, 1}, dy[4] = {0, 1, -1, 0};
    struct S
    {
        int x, y, t;            // current state
    }tmp;
    queue<struct S> que;
    
    int main()
    {
        cin >> n;
        for(int i = 0; i < 310; i++)
            for(int j = 0; j < 310; j++)
                s[i][j] = INF;
        int a, b, c;
        for(int i = 0; i < n; i++)
        {
            cin >> a >> b >> c;
            s[a][b] = c;
        }
    
        que.push({0, 0, 0});
        while(que.size())
        {
            tmp = que.front();
            que.pop();
            if(s[tmp.x][tmp.y] == INF)
            {
                bool bad = false;
                for(int i = 0; i < 4; i++)
                {
                    int nx = tmp.x + dx[i], ny = tmp.y + dy[i];
                    if(nx >= 0 && nx <= 300 && ny >= 0 && ny <= 300 && s[nx][ny] != INF )    // 当前位置安全当且仅当当前位置及其上下左右相邻格子流星坠落时间均为INF
                        bad = true;
                }
                if(!bad)
                {
                    ans = tmp.t;
                    break;
                }
            }
    
            for(int i = 0; i < 4; i++)
            {
                int nx = tmp.x + dx[i], ny = tmp.y + dy[i];
                if(nx >= 0 && nx <= 300 && ny >= 0 && ny <= 300 && tmp.t + 1 < s[nx][ny])    //此处有错误,可以移动到300x300场地以外的地方(300x300只是限制流星出现的范围)
                {                                               //本文后面的代码已经修正了这个错误
                    bool bad = false;
                    for(int j = 0; j < 4; j++)                              //检查移动到的目标格子上下左右是否已经有流星,若有则该格子已经烧焦了
                    {
                        int nnx = nx + dx[j], nny = ny + dy[j];
                        if(nnx >= 0 && nnx <= 300 && nny >= 0 && s[nnx][nny] != INF && nny <= 300 && tmp.t + 1 >= s[nnx][nny])
                        {
                            bad = true;
                            break;
                        }
                    }
                    if(!bad)
                        que.push({nx, ny, tmp.t + 1});
                }
            }
        }
    
        printf("%d
    ", ans);
    
        return 0;
    }

    TLE6个点.可以看出来写出这段代码已经够痛苦了.(写代码痛苦程度与使用bad数量成正比)

    有一个简单的剪枝念头就是只向下或者向右移动(y轴与数学的坐标系反向),但是很容易举出来反例.稍微思考一下后发现只需要将已经访问过的坐标标记并且以后不再访问即可,因为随着时间的增长所有格子的情况对于最终结果的影响只可能变坏或者不变,后者仅出现在该格子为终点的情况,不需要考虑.

    除此之外,有一个简化代码的方案免去了所有的判断对于上下左右的格子的条件限制.见最终代码.

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int INF = 100000000;
    
    int n, s[310][310], ans = -1;        // burn time
    bool vis[310][310];        // don't go back, there's no good
    int dx[4] = {-1, 0, 0, 1}, dy[4] = {0, 1, -1, 0};
    struct S
    {
        int x, y, t;            // current state
    }tmp;
    queue<struct S> que;
    
    int main()
    {
        cin >> n;
        for(int i = 0; i < 310; i++)
            for(int j = 0; j < 310; j++)
                s[i][j] = INF;
    /*
    这个方案就是对输入数据进行一些预处理,使得在输入结束后所有坐标上的值直接地表示在何时刻及之后该坐标不可用.
    所以在之后的搜索中只需要对当前的格子情况做出判断就可以了,不再需要顾虑上下左右的格子.
    */
    int a, b, c; for(int i = 0; i < n; i++) { cin >> a >> b >> c; s[a][b] = min(s[a][b], c); if(a > 0) s[a - 1][b] = min(s[a - 1][b], c); if(b > 0) s[a][b - 1] = min(s[a][b - 1], c); s[a + 1][b] = min(s[a + 1][b], c); s[a][b + 1] = min(s[a][b + 1], c); } que.push({0, 0, 0}); while(que.size()) { tmp = que.front(); que.pop(); if(s[tmp.x][tmp.y] == INF) { // bool bad = false; // for(int i = 0; i < 4; i++) // { // int nx = tmp.x + dx[i], ny = tmp.y + dy[i]; // if(nx >= 0 && ny >= 0 && s[nx][ny] != INF ) // bad = true; // } // if(!bad) // { ans = tmp.t; break; // } } for(int i = 0; i < 4; i++) { int nx = tmp.x + dx[i], ny = tmp.y + dy[i]; if(nx >= 0 && ny >= 0 && tmp.t + 1 < s[nx][ny]) { // bool bad = false; // for(int j = 0; j < 4; j++) // { // int nnx = nx + dx[j], nny = ny + dy[j]; // if(nnx >= 0 && nny >= 0 && s[nnx][nny] != INF && tmp.t + 1 >= s[nnx][nny]) // { // bad = true; // break; // } // } // if(!bad) if(!vis[nx][ny]) que.push({nx, ny, tmp.t + 1}); vis[nx][ny] = true; } } } printf("%d ", ans); return 0; }
  • 相关阅读:
    python中向函数传递列表
    python中函数与while循环结合
    python中使用函数和不使用函数程序的比较
    python中函数返回字典
    python中传递任意数量的实参 (收集参数)
    python中给函数添加返回字典中的可选键值对
    python中禁止函数修改列表
    python中结合使用位置实参和任意数量实参(收集参数)
    SAP所有用户出口列表(4.6C)(续)
    常见的abap面试题目,请大家对照学习
  • 原文地址:https://www.cnblogs.com/Gaomez/p/14039388.html
Copyright © 2020-2023  润新知