【搜索】多源BFS专题,存用特殊符号分隔的网格图的方法

学习资料
1.如何使用「多源 BFS」降低时间复杂度
2.【Leetcode】图的多源BFS详解
3.多源BFS 矩阵距离
4.算法讲解062【必备】宽度优先遍历及其扩展


标准多源BFS

求网格图被全部遍历所需轮数(层数)

994. 腐烂的橘子 - 力扣(LeetCode)

using PII = pair<int, int>;
class Solution {
public:
    vector<vector<int>> g;
    int n, m, res;
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    int bfs()
    {
        queue<PII> q;
        int cnt1 = 0;//记录新鲜橘子的个数
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                if (g[i][j] == 2) q.push({i, j});
                else if (g[i][j] == 1) cnt1++;
        if (cnt1 == 0) return 0;

        while (q.size() && cnt1) //cnt1如果提前为0了那就不需要循环了,提前返回
        {
            int len = q.size();
            while (len--)
            {
                auto [x, y] = q.front();
                q.pop();
                
                for (int i = 0; i < 4; i++)
                {
                    int a = x + dx[i], b = y + dy[i];
                    if (a < 0 || a >= n || b < 0 || b >= m) continue;
                    if (g[a][b] != 1) continue;
                    cnt1--;
                    g[a][b] = 2;
                    q.push({a, b}); 
                }
            }
            res++;
        }
        return cnt1 ? -1 : res;//cnt1不为0说明有新鲜橘子没腐烂,返回-1,为0就返回层数
    }
    int orangesRotting(vector<vector<int>>& grid) {
        g = grid, n = grid.size(), m = grid[0].size();
        return bfs();
    }
};

宜居星球改造计划

image

样例1
输入

YES YES NO
NO NO NO
YES NO NO

输出

2

说明:
经过 2 个太阳日,完成宜居改造。
样例2
输入

YES NO NO NO
NO NO NO NO
NO NO NO NO
NO NO NO NO

输出

6

说明:
经过 6 个太阳日,可完成改造

样例3
输入

NO NA

输出

-1

说明:
无改造初始条件,无法进行改造

样例4
输入

YES NO NO YES
NO NO YES NO
NO YES NA NA
YES NO NA NO

输出

-1

说明:
-1 ,右下角的区域,被周边三个死亡区挡住,无法实现改造


C++代码

// Problem: #OD293. 宜居星球改造计划
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 多源BFS
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
using PII = pair<int, int>;

const int N = 10;

int n, m;
vector<vector<string>> g(N);
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int bfs()
{
    queue<PII> q;
    int ycnt = 0; // 记录网格图中yes的个数
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (g[i][j] == "YES")
            {
                q.push({i, j});
            }
        }
    }
    int res = 0;
    while (q.size())
    {
        int len = q.size();
        while (len--)
        {
            auto [x, y] = q.front();
            q.pop();
            ycnt++;
            if (ycnt == n * m) return res; // 全部变成yes,直接返回层数

            for (int i = 0; i < 4; i++)
            {
                int a = x + dx[i], b = y + dy[i];
                if (a < 0 || a >= n || b < 0 || b >= m) continue;
                if (g[a][b] == "YES" || g[a][b] == "NA") continue;
                g[a][b] = "YES";
                q.push({a, b});
            }
        }
        res++;
    }
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            if (g[i][j] == "NO") return -1;
    return res;
}

void get() //存网格图
{
    string s;
    while (getline(cin, s))
    {
        for (int i = 0; i < s.size(); i++)
        {
            if (isalpha(s[i]))
            {
                int j = i;
                string word;
                while (j < s.size() && isalpha(s[j])) word += s[j++];
                g[n].push_back(word);
                i = j;
            }
        }
        n++;
    }
    m = g[0].size();
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    get();
    cout << bfs();
    return 0;
}

计算疫情扩散时间

image
样例1
输入

1,0,1,0,0,0,1,0,1

输出

2

说明:
1天以后,地图中仅剩余中心点未被感染; 2天以后,全部被感染

样例2
输入

1,1,1,1,1,1,1,1,1

输出

-1

说明:
全部都感染


C++代码

// Problem: #OD239. 计算疫情扩散时间
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
using PII = pair<int, int>;

const int N = 210;

int cnt, n;
vector<vector<int>> g(N); // 只能初始化一维,千万别初始化两维,不然读不对

void get() //把字符串形式的网格图存入二维矩阵
{
    string str;
    getline(cin, str);

    for (char x : str)
        if (x != ',') n++;
    n = sqrt(n);

    int k = 0;
    for (char &x : str)
    {
        if (x != ',')
        {
            int val = x - '0';
            g[k].push_back(val);
            cnt++; //记录本行读了几个数了(从1开始)
            if (cnt == n) //读完一行了
            {
                k++; // 读下一行
                cnt = 0; // 计数器归0
            }
        }
    }
}

int bfs()
{
    int cnt1 = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (g[i][j] == 1) cnt1++;
        }
    }
    if (cnt1 == n * n || cnt1 == 0) return -1;

    queue<PII> q;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (g[i][j] == 1)
            {
                q.push({i, j});
            }
        }
    }
    int ycnt = 0, res = 0;
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    while (q.size())
    {
        int len = q.size();
        while (len--)
        {
            auto [x, y] = q.front();
            q.pop();
            ycnt++; // 感染区域+1
            if (ycnt == n * n) return res; // 全部感染,返回层数

            for (int i = 0; i < 4; i++)
            {
                int a = x + dx[i], b = y + dy[i];
                if (a < 0 || a >= n || b < 0 || b >= n) continue;
                if (g[a][b] == 1) continue;
                g[a][b] = 1;
                q.push({a, b});
            }
        }
        res++; // 层数
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    get();
    cout << bfs();
    return 0;
}

debug版代码(方便输出矩阵状态)

// Problem: #OD239. 计算疫情扩散时间
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 1000 ms

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
using i64 = long long;
using PII = pair<int, int>;

const int N = 210;

int cnt, n;
int a[N * N];
vector<vector<int>> g(N); // 只能初始化一维,千万别初始化两维,不然读不对

void print()
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cout << g[i][j] << ' ';
        }
        cout << '\n';
    }
}

void get()
{
    string str;
    getline(cin, str);

    for (char x : str)
        if (x != ',') n++;
    n = sqrt(n);

    int k = 0;
    for (char &x : str)
    {
        if (x != ',')
        {
            int val = x - '0';
            g[k].push_back(val);
            cnt++;
            if (cnt == n)
            {
                k++;
                cnt = 0;
            }
        }
    }
}

int bfs()
{
    int cnt1 = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (g[i][j] == 1) cnt1++;
        }
    }
    if (cnt1 == n * n || cnt1 == 0) return -1;

    queue<PII> q;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (g[i][j] == 1)
            {
                q.push({i, j});
            }
        }
    }
    int ycnt = 0, res = 0;
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    while (q.size())
    {
        int len = q.size();
        while (len--)
        {
            auto [x, y] = q.front();
            q.pop();
            ycnt++; // 感染区域+1
            if (ycnt == n * n) return res; // 全部感染,返回层数

            for (int i = 0; i < 4; i++)
            {
                int a = x + dx[i], b = y + dy[i];
                if (a < 0 || a >= n || b < 0 || b >= n) continue;
                if (g[a][b] == 1) continue;
                g[a][b] = 1;
                q.push({a, b});
            }
        }
        res++; // 层数
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    get();
    // print();
    cout << bfs();
    return 0;
}

有步数限制的多源BFS

跳马(多源BFS变种,每个起点有步数限制)

image

image

补充几个测试样例
输入

3 2
. .
2 .
. .

输出

0

输入

3 5
4 7 . 4 8
4 7 4 4 .
7 . . . .

输出

17

输入

3 4
. . . .
. 2 . .
. . . .

输出

0

输入

3 4
. . . .
. 2 2 .
. . . .

输出

-1

本题很坑爹的地方在于,输入的字符串还用空格分开,所以读入时不能用读连续字符串的方法,例如
读字符串数组(n * m字符矩阵,中间无空格)char g[N][N]写法

for (int i = 0; i < n; i++) scanf("%s", g[i]);//读取
for (int i = 0; i < n; i++) printf("%s\n", g[i]);//输出写法1
for (int i = 0; i < n; i++) puts(g[i]);//输出写法2

只能当成矩阵一个一个读,就和读int型二维矩阵一样。

分析:暴力法
本题数据量不大且因为每个点有移动步数限制,所以采用每个点都搜一次的方法,搜完一个点,dist数组中存的就是该点到其余所有位置的最小步数,更新到total数组中,如果dist[i][j] == -1,或dist[i][j] > step,则说明该点不可达,在total中标记为-1.
最后遍历一遍total数组,把所有-1改成正无穷,再取最小值输出即可。

C++代码

/**
 * Created by Tshaxz on 25-1-16.
 */
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int, int> PII;

const int N = 30, INF = 0x3f3f3f3f;

int n, m;
char g[N][N];
int dist[N][N], total[N][N];
int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};

void bfs(int x, int y)
{
    memset(dist, -1, sizeof dist);
    queue<PII> q;
    dist[x][y] = 0;
    q.push({x, y});

    while (q.size())
    {
        auto [x, y] = q.front();
        q.pop();

        for (int i = 0; i < 8; i++)
        {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= m) continue;
            if (dist[a][b] != -1) continue;
            dist[a][b] = dist[x][y] + 1;
            q.push({a, b});
        }

    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> g[i][j];

    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            if (g[i][j] != '.')
            {
                int step = g[i][j] - '0';
                bfs(i, j);
                for (int x = 0; x < n; x++)
                    for (int y = 0; y < m; y++)
                    {
                        if (dist[x][y] > step) total[x][y] = -1;
                        else if (total[x][y] != -1) total[x][y] += dist[x][y];
                    }
            }


    int res = INF;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
        {
            if (total[i][j] == -1) total[i][j] = INF;
            res = min(res, total[i][j]);
        }

    if (res == INF) res = -1;
    cout << res << '\n';
    return 0;
}

debug版代码,可以输出dist与total数组查看

/**
 * Created by Tshaxz on 25-1-16.
 */
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

typedef pair<int, int> PII;

const int N = 30, INF = 0x3f3f3f3f;

int n, m;
char g[N][N];
int dist[N][N], total[N][N];
int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};

void bfs(int x, int y)
{
    memset(dist, -1, sizeof dist);
    queue<PII> q;
    dist[x][y] = 0;
    q.push({x, y});

    while (q.size())
    {
        auto [x, y] = q.front();
        q.pop();

        for (int i = 0; i < 8; i++)
        {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= n || b < 0 || b >= m) continue;
            if (dist[a][b] != -1) continue;
            dist[a][b] = dist[x][y] + 1;
            q.push({a, b});
        }

    }
}

// void print1()
// {
//     printf("dist: \n");
//     for (int i = 0; i < n; i++)
//     {
//         for (int j = 0; j < m; j++) printf("\t%d\t", dist[i][j]);
//         puts("");
//     }
// }

// void print2()
// {
//     printf("total:\n");
//     for (int i = 0; i < n; i++)
//     {
//         for (int j = 0; j < m; j++) printf("\t%d\t", total[i][j]);
//         puts("");
//     }
// }

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> g[i][j];

    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            if (g[i][j] != '.')
            {
                int step = g[i][j] - '0';
                bfs(i, j);
                // printf("当前的马:g[%d][%d]:%c\n", i, j, g[i][j]);
                // print1();
                for (int x = 0; x < n; x++)
                    for (int y = 0; y < m; y++)
                    {
                        if (dist[x][y] > step) total[x][y] = -1;
                        else if (total[x][y] != -1) total[x][y] += dist[x][y];
                    }
                
                // print2();
            }
            
            
    int res = INF;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
        {
            if (total[i][j] == -1) total[i][j] = INF;
            res = min(res, total[i][j]);
        }
            
    if (res == INF) res = -1;
    cout << res << '\n';
    return 0;
}
posted @   Tshaxz  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
Language: HTML
点击右上角即可分享
微信分享提示