Leetcode刷题——广度优先搜索与深度优先搜索题目(二)

135 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

广度优先搜索

1765. 地图中的最高点

题目 给你一个大小为 m x n 的整数矩阵 isWater ,它代表了一个由 陆地 和 水域 单元格组成的地图。

如果 isWater[i][j] == 0 ,格子 (i, j) 是一个 陆地 格子。 如果 isWater[i][j] == 1 ,格子 (i, j) 是一个 水域 格子。 你需要按照如下规则给每个单元格安排高度:

每个格子的高度都必须是非负的。 如果一个格子是是 水域 ,那么它的高度必须为 0 。 任意相邻的格子高度差 至多 为 1 。当两个格子在正东、南、西、北方向上相互紧挨着,就称它们为相邻的格子。(也就是说它们有一条公共边) 找到一种安排高度的方案,使得矩阵中的最高高度值 最大 。

请你返回一个大小为 m x n 的整数矩阵 height ,其中 height[i][j] 是格子 (i, j) 的高度。如果有多种解法,请返回 任意一个 。

示例 1:

在这里插入图片描述

输入:isWater = [[0,1],[0,0]]
输出:[[1,0],[2,1]]
解释:上图展示了给各个格子安排的高度。
蓝色格子是水域格,绿色格子是陆地格。

示例 2:

在这里插入图片描述

输入:isWater = [[0,0,1],[1,0,0],[0,0,0]]
输出:[[1,1,0],[0,1,1],[1,2,2]]
解释:所有安排方案中,最高可行高度为 2 。
任意安排方案中,只要最高高度为 2 且符合上述规则的,都为可行方案。

代码

提示:

m == isWater.length
n == isWater[i].length
1 <= m, n <= 1000
isWater[i][j] 要么是 0 ,要么是 1 。
至少有 1 个水域格子。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ma… 代码

class Solution {
public:
    int mx[4] = {0, 0, 1, -1};
    int my[4] = {1, -1, 0, 0};
    vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {
        int m=isWater.size(), n=isWater[0].size();
        vector<vector<int>> v(m, vector<int>(n, -1));
        queue<pair<int, int>> q;
        for(int i=0; i < m; i++){
            for(int j=0; j < n; j++){
                if(isWater[i][j]){
                    q.emplace(i, j);
                    v[i][j] = 0;
                }
            }
        }
        while(!q.empty()){
            auto &p = q.front();
            for(int i=0; i < 4; i++){
                int x = p.first+mx[i], y = p.second+my[i];
                if(x >= 0 && x < m && y >= 0 && y < n && v[x][y] == -1){
                    q.emplace(x, y);
                    v[x][y] = v[p.first][p.second]+1;
                }
            }
            q.pop();
        }
        return v;
    }
};

2045. 到达目的地的第二短时间

题目 城市用一个 双向连通 图表示,图中有 n 个节点,从 1 到 n 编号(包含 1 和 n)。图中的边用一个二维整数数组 edges 表示,其中每个 edges[i] = [ui, vi] 表示一条节点 ui 和节点 vi 之间的双向连通边。每组节点对由 最多一条 边连通,顶点不存在连接到自身的边。穿过任意一条边的时间是 time 分钟。

每个节点都有一个交通信号灯,每 change 分钟改变一次,从绿色变成红色,再由红色变成绿色,循环往复。所有信号灯都 同时 改变。你可以在 任何时候 进入某个节点,但是 只能 在节点 信号灯是绿色时 才能离开。如果信号灯是 绿色 ,你 不能 在节点等待,必须离开。

第二小的值 是 严格大于 最小值的所有值中最小的值。

例如,[2, 3, 4] 中第二小的值是 3 ,而 [2, 2, 4] 中第二小的值是 4 。 给你 n、edges、time 和 change ,返回从节点 1 到节点 n 需要的 第二短时间 。

注意:

你可以 任意次 穿过任意顶点,包括 1 和 n 。 你可以假设在 启程时 ,所有信号灯刚刚变成 绿色 。

示例 1:

在这里插入图片描述

输入:n = 5, edges = [[1,2],[1,3],[1,4],[3,4],[4,5]], time = 3, change = 5
输出:13
解释:
上面的左图展现了给出的城市交通图。
右图中的蓝色路径是最短时间路径。
花费的时间是:
- 从节点 1 开始,总花费时间=0
- 1 -> 4:3 分钟,总花费时间=3
- 4 -> 5:3 分钟,总花费时间=6
因此需要的最小时间是 6 分钟。

右图中的红色路径是第二短时间路径。
- 从节点 1 开始,总花费时间=0
- 1 -> 3:3 分钟,总花费时间=3
- 3 -> 4:3 分钟,总花费时间=6
- 在节点 4 等待 4 分钟,总花费时间=10
- 4 -> 5:3 分钟,总花费时间=13
因此第二短时间是 13 分钟。   

示例 2:

在这里插入图片描述

输入:n = 2, edges = [[1,2]], time = 3, change = 2
输出:11
解释:
最短时间路径是 1 -> 2 ,总花费时间 = 3 分钟
最短时间路径是 1 -> 2 -> 1 -> 2 ,总花费时间 = 11 分钟

提示:

2 <= n <= 104
n - 1 <= edges.length <= min(2 * 104, n * (n - 1) / 2)
edges[i].length == 2
1 <= ui, vi <= n
ui != vi
不含重复边
每个节点都可以从其他节点直接或者间接到达
1 <= time, change <= 103

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/se… 分析 使用广度优先搜索获取最短距离和次短距离,这里使用path数组来表示,path[i][0]为按照最短路径的时间,path[i][1]为按照次短路径的时间,只要在广度优先遍历的时候维护最短路径和次短路径的时间即可。 代码

class Solution {
public:
    int secondMinimum(int n, vector<vector<int>>& edges, int time, int change) {
        vector<vector<int>> graph(n + 1);
        for (auto &e : edges) {
            graph[e[0]].push_back(e[1]);
            graph[e[1]].push_back(e[0]);
        }
        vector<vector<int>> path(n + 1, vector<int>(2, INT_MAX));
        path[1][0] = 0;
        queue<pair<int, int>> q;
        q.push({1, 0});
        while (!q.empty()) {
            auto [cur, timestamp] = q.front();
            q.pop();
            if(cur == n && path[cur][1] != INT_MAX){
                return path[n][1];
            }
            if (timestamp % (2 * change) >= change) {
                timestamp += (2 * change - timestamp % (2 * change));
            }
            timestamp += time;
            for (auto next : graph[cur]) {
                if (timestamp < path[next][0]) {
                    path[next][0] = timestamp;
                    q.push({next, timestamp});
                } else if (timestamp > path[next][0] && timestamp < path[next][1]) {
                    path[next][1] = timestamp;
                    q.push({next, timestamp});
                }
            }
        }
        return -1;
    }
};

深度优先搜索

1219. 黄金矿工

题目 你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 m * n 的网格 grid 进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是 0。

为了使收益最大化,矿工需要按以下规则来开采黄金:

每当矿工进入一个单元,就会收集该单元格中的所有黄金。 矿工每次可以从当前位置向上下左右四个方向走。 每个单元格只能被开采(进入)一次。 不得开采(进入)黄金数目为 0 的单元格。 矿工可以从网格中 任意一个 有黄金的单元格出发或者是停止。

示例 1:

输入:grid = [[0,6,0],[5,8,7],[0,9,0]] 输出:24 解释: [[0,6,0], [5,8,7], [0,9,0]] 一种收集最多黄金的路线是:9 -> 8 -> 7。

示例 2:

输入:grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]] 输出:28 解释: [[1,0,7], [2,0,6], [3,4,5], [0,3,0], [9,0,20]] 一种收集最多黄金的路线是:1 -> 2-> 3 -> 4 -> 5 -> 6 -> 7。

提示:

1 <= grid.length, grid[i].length <= 15 0 <= grid[i][j] <= 100 最多 25 个单元格中有黄金。

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/pa… 代码 这是一道很常规的深度优先搜索题目

class Solution {
public:
    int maxn=0;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    void dfs(vector<vector<int>>& grid, int x, int y, int cnt){
        cnt += grid[x][y];
        maxn = max(cnt, maxn);
        for(int i=0; i < 4; i++){
            int tx = x+dx[i], ty = y+dy[i];
            if(grid[tx][ty] && tx >= 0 && tx < grid.size() && ty >= 0 && ty < grid[0].size()){
                int tmp = grid[tx][ty];
                grid[tx][ty] = 0;
                dfs(grid, tx, ty, cnt);
                grid[tx][ty] = tmp;
            }
        }
    }
    int getMaximumGold(vector<vector<int>>& grid) {
        for(int i=0; i < grid.size(); i++){
            for(int j=0; j < grid[0].size(); j++){
                if(grid[i][j]!=0){
                    int tmp = grid[i][j];
                    grid[i][j] = 0;
                    
                    dfs(grid, i, j, 0);
                    grid[i][j] = tmp;
                }
            }
        }
        return maxn;
    }
};