Bootstrap

刷题 - 图论

1 | bfs/dfs | 网格染色

200. 岛屿数量

  • 访问到马上就染色(将visited标为 true)
  • auto [cur_x, cur_y] = que.front(); 结构化绑定(C++17)
  • 也可以不使用 visited数组,直接修改原始数组
  • 时间复杂度: O(n * m),最多将 visited 数组全部标为 true
  • 空间复杂度:考虑visited 数组的话就是 O(n*m), 原地修改就是 O(min(n, m)) 只考虑递归栈深度(DFS)或队列最大深度(BFS),栈深度或队列最多为网格的一边大小

在这里插入图片描述

[模板][dfs][染色]

class Solution {
public:
    const int direction[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
    // 为什么 dfs 看起来好像没有终止条件?因为已经通过代码保证传入的节点都是合法节点
    void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
        for (int i = 0; i < 4; ++i) {
            int next_x = x + direction[i][0], next_y = y + direction[i][1];
            if (next_x < 0 || next_x >= grid.size() || \
                next_y < 0 || next_y >= grid[0].size() || \
                grid[next_x][next_y] == '0' || \
                visited[next_x][next_y] == true) {
                continue;
            }
            visited[next_x][next_y] = true;
            dfs(grid, visited, next_x, next_y);
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited(n, vector<bool>(m, false));
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == '1' && visited[i][j] == false) {
                    ++ans;
                    visited[i][j] = true;
                    dfs(grid, visited, i, j);
                }
            }
        }
        return ans;
    }
};
class Solution {
public:
    const int direction[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
    // 不管节点是否合法,上来就dfs,然后在终止条件的地方进行判断
    // 效率会略低一些
    void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
        if (grid[x][y] == '0' || visited[x][y]) return;
        visited[x][y] = true;
        for (int i = 0; i < 4; ++i) {
            int next_x = x + direction[i][0], next_y = y + direction[i][1];
            if (next_x < 0 || next_x >= grid.size() || \
                next_y < 0 || next_y >= grid[0].size()) {
                continue;
            }
            dfs(grid, visited, next_x, next_y);
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited(n, vector<bool>(m, false));
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == '1' && visited[i][j] == false) {
                    ++ans;
                    dfs(grid, visited, i, j);
                }
            }
        }
        return ans;
    }
};

[模板][bfs][染色]

class Solution {
public:
    const int direction[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
    void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
        queue<pair<int,int>> que;
        visited[x][y] = true;   // 在加入队列之前将其标为访问过
        que.push(make_pair(x, y));
        while (!que.empty()) {
            auto [cur_x, cur_y] = que.front();  // 结构化绑定
            que.pop();
            for (int i = 0; i < 4; ++i) {
                int next_x = cur_x + direction[i][0], next_y = cur_y + direction[i][1];
                if (next_x < 0 || next_x >= grid.size() || \
                    next_y < 0 || next_y >= grid[0].size() || \
                    grid[next_x][next_y] == '0' || \
                    visited[next_x][next_y] == true) {
                    continue;
                }
                visited[next_x][next_y] = true;
                que.push(make_pair(next_x, next_y));
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited(n, vector<bool>(m, false));
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == '1' && visited[i][j] == false) {
                    ++ans;
                    bfs(grid, visited, i, j);
                }
            }
        }
        return ans;
    }
};

在这里插入图片描述

类似题目

695. 岛屿的最大面积

在 染色的时候统计面积即可

visited[next_x][next_y] = true;
++area;

130. 被围绕的区域

在这里插入图片描述

两个步骤:

  • 从地图周边出发,将 ‘O’ 都做上标记,visited = true
  • 然后再遍历一遍地图,遇到 ‘O’ 且没做过标记的,那么都是地图中间的 ‘O’ ,全部改成 ‘X’ 就行

417. 太平洋大西洋水流问题

在这里插入图片描述

class Solution {
public:
    // 太平洋:i == 0 || j == 0
    // 大西洋:i == m-1 || j == n-1
    // 方式1:从每个节点出发,如果可以到达太平洋且大西洋,记录结果
    // 方式2:从四周出发,逆向找可达节点

    int direction[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
    // array<array<int, 2>, 4> direction = {-1, 0, 0, -1, 1, 0, 0, 1};
    void dfs(const vector<vector<int>>& heights, vector<vector<bool>>& visited, int x, int y) {
        for (int i = 0; i < 4; ++i) {
            int next_x = x + direction[i][0], next_y = y + direction[i][1];
            if (next_x < 0 || next_x >= heights.size() || \
                next_y < 0 || next_y >= heights[0].size() || \
                visited[next_x][next_y] == true ||
                heights[next_x][next_y] < heights[x][y]) {
                    continue;
            }
            visited[next_x][next_y] = true;
            dfs(heights, visited, next_x, next_y);
        }
    }

    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        int m = heights.size(), n = heights[0].size();
        vector<vector<bool>> pacific_visited(m, vector<bool>(n, false));
        vector<vector<bool>> atlantic_visited(m, vector<bool>(n, false));
        
        for (int i = 0; i < n; ++i) {
            if (pacific_visited[0][i] == false) {
                pacific_visited[0][i] = true;
                dfs(heights, pacific_visited, 0, i);
            }
        }
        for (int i = 1; i < m; ++i) {
            if (pacific_visited[i][0] == false) {
                pacific_visited[i][0] = true;
                dfs(heights, pacific_visited, i, 0);
            }
        }

        for (int i = 0; i < n; ++i) {
            if (atlantic_visited[m-1][i] == false) {
                atlantic_visited[m-1][i] = true;
                dfs(heights, atlantic_visited, m-1, i);
            }
        }
        for (int i = 0; i < m - 1; ++i) {
            if (atlantic_visited[i][n-1] == false) {
                atlantic_visited[i][n-1] = true;
                dfs(heights, atlantic_visited, i, n-1);
            }
        }

        vector<vector<int>> result;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (pacific_visited[i][j] && atlantic_visited[i][j]) {
                    result.push_back({i,j});
                }
            }
        }

        return result;
    }
};

建造最大工岛

在这里插入图片描述

2 | bfs | 无权最短路径

  • 如果只需要路径长度,每次先获取队列的长度然后再往外扩展,或者可以采用使用 int 类型的 visited 数组来记录路径长度
  • 如果需要输出路径,需要记录节点的 前驱节点,最后从终点到起点获取前驱节点从而输出路径

[模板] 获取最短路径长度 - 记录steps

int bfs_shortest_path_length(const vector<vector<int>>& graph, int start, int goal) {
    int n = graph.size(); // 节点的总数
    queue<int> q; // 队列用于BFS
    vector<bool> visited(n, false); // 记录已经访问过的节点
    q.push(start);
    visited[start] = true;
    int steps = 0; // 用于记录步数
    while (!q.empty()) {
        int que_size = q.size();
        while (que_size--) {
            int node = q.front();
            q.pop();
            if (node == goal) {
                return steps;
            }
            for (int neighbor : graph[node]) {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    q.push(neighbor);
                }
            }
        }
        ++steps;
    }
    return -1;
}

[模板] 获取最短路径 - 记录前驱节点

// 使用BFS寻找从start到goal的最短路径,并使用前驱数组记录路径
vector<int> bfs_shortest_path(const vector<vector<int>>& graph, int start, int goal) {
    int n = graph.size(); // 节点的总数
    vector<int> prev(n, -1); // 用于记录前驱节点,初始化为-1表示未访问
    queue<int> q; // 队列用于BFS
    unordered_set<int> visited; // 记录已经访问过的节点
    // 初始化BFS
    q.push(start);
    visited.insert(start);
    // 开始BFS
    while (!q.empty()) {
        int node = q.front();
        q.pop();
        // 如果找到了目标节点,回溯路径
        if (node == goal) {
            vector<int> path;
            for (int at = goal; at != -1; at = prev[at]) {
                path.push_back(at);
            }
            reverse(path.begin(), path.end());
            return path; // 返回从start到goal的路径
        }
        // 遍历当前节点的所有邻居
        for (int neighbor : graph[node]) {
            if (visited.find(neighbor) == visited.end()) {
                // 如果邻居节点未被访问,标记为已访问并记录前驱节点
                visited.insert(neighbor);
                prev[neighbor] = node;
                q.push(neighbor);
            }
        }
    }

    // 如果没有找到路径,返回空路径
    return {};
}

类似题目

[最短路径长度]1926. 迷宫中离入口最近的出口

在这里插入图片描述

class Solution {
public:
    int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {
        int m = maze.size(), n = maze[0].size();
        int x = entrance[0], y = entrance[1];
        queue<pair<int, int>> que;
        maze[x][y] = '+';
        que.push({x, y});
        // 定义四个方向:上、下、左、右
        const int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        int steps = 0;
        // 广度优先搜索
        while (!que.empty()) {
            int q_size = que.size();
            for (int i = 0; i < q_size; ++i) {
                auto [cur_x, cur_y] = que.front();
                que.pop();
                for (int d = 0; d < 4; ++d) {
                    int next_x = cur_x + dir[d][0];
                    int next_y = cur_y + dir[d][1];
                    // 检查边界条件
                    if (next_x < 0 || next_x >= m || \
                        next_y < 0 || next_y >= n || \
                        maze[next_x][next_y] == '+') {
                        continue;
                    }
                    // 如果到达边界并且不是入口,返回步数
                    if (next_x == 0 || next_x == m - 1 ||\
                         next_y == 0 || next_y == n - 1) {
                        return steps + 1;
                    }
                    // 标记为已访问,并将其加入队列
                    maze[next_x][next_y] = '+';
                    que.push({next_x, next_y});
                }
            }
            ++steps;
        }
        // 如果没有找到出口,返回 -1
        return -1;
    }
};

[最短路径长度] 433. 最小基因变化

在这里插入图片描述

class Solution {
public:
    bool canConvert(const string& s1, const string& s2) {
        int diffCount = 0;
        for (int i = 0; i < s1.size(); ++i) {
            if (s1[i] != s2[i]) {
                if (++diffCount > 1) return false;
            }
        }
        return true;
    }

    int minMutation(string startGene, string endGene, vector<string>& bank) {
        if (startGene == endGene) return 0;
        int n = bank.size();
        int startIndex = -1, endIndex = -1;
        // 查找 startGene 和 endGene 在 bank 中的位置
        for (int i = 0; i < n; ++i) {
            if (bank[i] == endGene) endIndex = i;
            else if (bank[i] == startGene) startIndex = i;
        }
        // 如果终止基因不在 bank 中,直接返回 -1
        if (endIndex == -1) return -1;
        // 如果起始基因不在 bank 中,则将其作为附加节点处理
        if (startIndex == -1) startIndex = n;
        // 构建邻接表
        vector<list<int>> adj(n + 1);
        // 如果 startGene 不在 bank 中,构造与它相邻的节点
        if (startIndex == n) {
            for (int i = 0; i < n; ++i) {
                if (canConvert(startGene, bank[i])) {
                    adj[n].push_back(i);
                }
            }
        }
        // 构造 bank 中基因的邻接关系
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (canConvert(bank[i], bank[j])) {
                    adj[i].push_back(j);
                    adj[j].push_back(i);
                }
            }
        }
        // BFS 进行最短路径搜索
        queue<int> q;
        vector<bool> visited(n + 1, false);
        q.push(startIndex);
        visited[startIndex] = true;
        int steps = 0;
        while (!q.empty()) {
            int que_size = q.size();
            while (que_size--) {
                int node = q.front();
                q.pop();
                // 找到终止基因,返回步数
                if (node == endIndex) return steps;
                // 遍历邻居节点
                for (int neighbor : adj[node]) {
                    if (!visited[neighbor]) {
                        visited[neighbor] = true;
                        q.push(neighbor);
                    }
                }
            }
            ++steps; // 每一轮BFS增加一步
        }
        // 如果找不到转换路径,返回 -1
        return -1;
    }
};

[最短路径长度]127. 单词接龙

在这里插入图片描述

和最小基因变化基本没区别

3 | dfs | 回溯 | 所有可能路径

[模板] 797. 所有可能的路径

  • 本题为有向无环图,如果存在环的话需要加一个 visited 数组
  • 时间复杂度:O(2^n),最坏情况下可能有 2 的 n 次方条路径,其中 n 是图的节点数。
  • 空间复杂度:O(n),递归深度最深为 n(即从 0 到目标节点的最长路径长度),以及存储路径所需的空间。
// 有向无环图求所有可行路径
// 如果存在环的话需要加一个 visited 数组
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void dfs(vector<vector<int>>& graph, int start) {
        if (start == graph.size() - 1) {
            result.push_back(path);
        }
        for (auto& next : graph[start]) {
            path.push_back(next);
            dfs(graph, next);
            path.pop_back();
        }
    }

    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        path.push_back(0);
        dfs(graph, 0);
        return result; 
    }
};

4 | 并查集 | 判断节点相连

[模板][有向图][无向图]

  • 并查集初始化:将所有节点的根节点初始化为节点本身
  • 在查询父节点的时候要执行路径压缩

有向边: 按方向合并

    struct UnionSet {
        vector<int> root;

        UnionSet(int n) : root(n, 0) {
            for (int i = 0; i < n; ++i) {
                root[i] = i;
            }
        }

        int Find(int u) {
            if (u == root[u]) return u;
            root[u] = Find(root[u]); // 路径压缩
            return root[u];
        }

        bool IsConnected(int u, int v) {
            return Find(u) == Find(v);
        }

        void Join(int u, int v) {
            int root_u = Find(u);
            int root_v = Find(v);
            root[root_v] = root_u;
        }
    };

无向边:按秩合并

  • 在并查集(Union-Find)的按秩合并中,通常用于表示树的高度(或深度)
  • 插入新连接的时候选择秩小的作为根节点
    在这里插入图片描述
  • 如果二者秩相同,秩需要加一
    在这里插入图片描述
struct UnionSet{
    vector<int> root;
    vector<int> rank;

    UnionSet(int n) : root(n, 0), rank(n, 1) {
        for (int i = 0; i < n; ++i) {
            root[i] = i;
        }
    }

    int Find(int u) {
        if (u == root[u]) return u;
        root[u] = Find(root[u]); // 路径压缩
        return root[u];
    }

    bool IsConnected(int u, int v) {
        return Find(u) == Find(v);
    }

    void Join(int u, int v) {
        int root_u = Find(u);
        int root_v = Find(v);
        if (rank[root_u] > rank[root_v]) {
            root[root_v] = root_u;
        } else if (rank[root_u] < rank[root_v]) {
            root[root_u] = root_v;
        } else {
            root[root_u] = root_v;
            rank[root_v]++;
        }
    }
};

类似题目

[无向边]684. 冗余连接

在这里插入图片描述

如果边的两个节点已经出现在同一个集合里,说明这条边的两个节点已经连在一起了,再加入这条边一定就出现环了。

    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        int n = edges.size();
        UnionSet union_set(n + 1);
        for (auto& edge : edges) {
            int u = edge[0], v = edge[1];
            if (union_set.IsConnected(u, v)) {
                return {u, v};
            } else {
                union_set.Join(u, v);
            }
        }
        return {};
    }

[有向边]685. 冗余连接 II

参考链接:【困难题简单做】分类讨论+并查集,击破hard
在这里插入图片描述

class Solution {
public:
    // 查找冗余的有向边
    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        int n = edges.size();
        vector<int> parent(n + 1, 0);
        vector<int> edge_to_remove1, edge_to_remove2;
        int two_in_node = -1;

        // 检测入度为2的情况
        for (auto& edge : edges) {
            int u = edge[0], v = edge[1];
            if (parent[v] == 0) {
                parent[v] = u;
            } else {
                // 发现入度为2的节点 v
                two_in_node = v;
                edge_to_remove1 = {parent[v], v}; // 之前的边
                edge_to_remove2 = {u, v}; // 当前的边
                break;
            }
        }

        // 重建并查集,处理有环的情况
        UnionSet union_set(n + 1);
        for (auto& edge : edges) {
            int u = edge[0], v = edge[1];
            if (edge == edge_to_remove2) {
                // 跳过第二条边,先验证第一条边
                continue;
            }
            if (union_set.IsConnected(u, v)) {
                // 出现环
                //  + 如果不存在入度为2的节点,只需要考虑环的问题
                //  + 说明正是[u,v]导致环的出现,删除 [u, v] 即可    
                if (two_in_node == -1) return edge;

                //  + 存在入度为 2 的节点
                //  + edge_to_remove1 必然属于这个环的一部分
                //  + 否则就会出现两条不合法的边了,不合题意
                //  + 因此我们删除两个不合法的交集,也即 edge_to_remove1
                return edge_to_remove1;
            }
            union_set.Join(u, v);
        }

        // 循环正常结束,说明没有出现环
        // 说明异常情况为:存在入度为 2 的节点
        // 删除最后一条导致入度为 2 的边,也即 edge_to_remove2
        return edge_to_remove2;
    }
};

5 | 拓扑排序 | 有向图 | 判断是否有环 | 无环图线性排序

[模板]207. 课程表

  • 遍历所有边,记录所有节点的入度,将入度为 0 的节点加入队列和结果数组
  • 当队列不为空时,从队列中弹出节点,遍历节点的所有邻居,将邻居的入度减1,如果此时邻居的入度为0,则将该邻居加入队列和结果数组
  • 队列为空,若结果数组的长度不等于节点个数,说明存在环,否则返回结果数组
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> indegrees(numCourses, 0);
        vector<list<int>> adj(numCourses);
        for (const auto& pre : prerequisites) {
            ++indegrees[pre[0]];
            adj[pre[1]].push_back(pre[0]);
        }
        int count = 0;
        queue<int> que;
        for (int i = 0; i < numCourses; ++i) {
            if (indegrees[i] == 0) {
                que.push(i);
                ++count;
            }
        }
        while (!que.empty()) {
            int cur = que.front();
            que.pop();
            for (auto next : adj[cur]) {
                if (--indegrees[next] == 0) {
                    que.push(next);
                    ++count;
                }
            }
        }
        return (count == numCourses);
    }
};

6 | Dijistra | 带正权图最短路径

[模板]参加科学大会

  • 使用 堆来存储当前最短路径节点 {节点到起点的距离, 节点索引}
  • 每次从优先队列中选择当前距离最小的未处理节点
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <list>
using namespace std;

int dijkstra(vector<list<pair<int, int>>>& adj_list) {
    int n = adj_list.size() - 1; 
    int start = 1, end = n;
    vector<int> costs(n + 1, INT_MAX);	// 记录距离
    vector<int> visited(n + 1, false);	// 标记是否找到最短路		
    // 堆存储 {节点到起点的距离,节点索引}
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    // 初始化
    costs[start] = 0;
    pq.push({0, start});

    while (!pq.empty()) {
    	// 每次从优先队列中选择当前距离最小的未处理节点
        int cost = pq.top().first;
        int cur = pq.top().second;
        pq.pop();
        if (visited[cur]) continue;	// 节点已经访问过,跳过
        visited[cur] = true;			// 未访问过,标记为访问过
        if (cur == end) return costs[end];	// 到达终点
        // 遍历相邻节点
        for (auto& [next, time] : adj_list[cur]) {
            if (!visited[next] && costs[cur] + time < costs[next]) {
            	// 找到更短的路径了   
                costs[next] = costs[cur] + time;
                pq.push({costs[next], next});
            }
        }
    }
    return -1;  // 如果没有路径到达终点
}

int main(int argc, char* argv[]) {
    if (argc == 1) return -1;
    freopen(argv[1], "r", stdin);
    int n, m;
    cin >> n >> m;  // n 个 车站 m 条公路
    vector<list<pair<int, int>>> adj_list(n + 1);
    for (int i = 0; i < m; ++i) {   // 输入 m 条边:起点 终点 和 时间
        int start, end, time;
        cin >> start >> end >> time;
        adj_list[start].push_back({end, time});
    }

    cout << dijkstra(adj_list) << endl;
    return 0;
}

7 | Floyd | 多源最短路 | 动态规划 | 适合稠密图且源点较多

[模板]97. 小明逛公园

在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <vector>
#include <list>
#include <climits>
using namespace std;

int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;

    vector<vector<vector<int>>> grid(n + 1, vector<vector<int>>(n + 1, vector<int>(n + 1, INT_MAX)));  // 因为边的最大距离是10^4
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid[p1][p2][0] = val;
        grid[p2][p1][0] = val; // 注意这里是双向图

    }
    // 开始 floyd
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (grid[i][k][k-1] == INT_MAX || grid[k][j][k-1] == INT_MAX) {
                    grid[i][j][k] = grid[i][j][k-1];
                } else {
                    grid[i][j][k] = min(grid[i][j][k-1], grid[i][k][k-1] + grid[k][j][k-1]);
                }      
            }
        }
    }
    // 输出结果
    int z, start, end;
    cin >> z;
    while (z--) {
        cin >> start >> end;
        if (grid[start][end][n] == INT_MAX) cout << -1 << endl;
        else cout << grid[start][end][n] << endl;
    }
}

空间优化

#include <iostream>
#include <vector>
#include <list>
#include <climits>
using namespace std;

int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;

    vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));  // 因为边的最大距离是10^4
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid[p1][p2] = val;
        grid[p2][p1] = val; // 注意这里是双向图

    }
    // 开始 floyd
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (grid[i][k] != INT_MAX && grid[k][j] != INT_MAX) {
                    grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j]);
                }      
            }
        }
    }
    // 输出结果
    int z, start, end;
    cin >> z;
    while (z--) {
        cin >> start >> end;
        if (grid[start][end] == INT_MAX) cout << -1 << endl;
        else cout << grid[start][end] << endl;
    }
}

8 | Prim | 贪心 | 最小生成树 | 花最小的代价连通所有的点

[模板] 53. 寻宝(第七期模拟笔试)

图-最小生成树-Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法

#include<iostream>
#include<vector>
#include <climits>

using namespace std;

int main() {
    int v, e;   // 顶点数和边数
    cin >> v >> e;
    
    vector<vector<int>> grid(v + 1, vector<int>(v + 1, INT_MAX));
    
    int x, y, k;    // 起点,终点和权值
    while (e--) {
        cin >> x >> y >> k;
        grid[x][y] = k;
        grid[y][x] = k;   // 无向图
    }
    
    // Prim算法部分
    vector<int> minDist(v + 1, INT_MAX);        // 所有节点到最小生成树的最小距离
    vector<bool> isInTree(v + 1, false);        // 是否已经在最小生成树中
    minDist[1] = 0;                             // 从第1个节点开始
    
    int result = 0;
    
    for (int i = 1; i <= v; i++) {
        int cur = -1;
        int minVal = INT_MAX;

        // 找到距离当前生成树最近的节点
        for (int j = 1; j <= v; j++) {
            if (!isInTree[j] && minDist[j] < minVal) {
                minVal = minDist[j];
                cur = j;
            }
        }

        // 如果没有找到合适的节点,则图不连通
        if (cur == -1) {
            cout << "Graph is not connected" << endl;
            return -1;
        }

        isInTree[cur] = true;
        result += minVal;  // 把该点与生成树相连的最小边的权值加入结果

        // 更新其他节点到生成树的最短距离
        for (int j = 1; j <= v; j++) {
            if (!isInTree[j] && grid[cur][j] != INT_MAX && grid[cur][j] < minDist[j]) {
                minDist[j] = grid[cur][j];
            }
        }
    }
    
    // 输出最小生成树的权重
    cout << result << endl;

    return 0;
}

;