【算法篇】图论类(1)(笔记)

目录

一、基础知识

[1. 图的种类](#1. 图的种类)

(1)有向图

(2)无向图

(3)加权有向图

[2. 图的构造](#2. 图的构造)

(1)邻接矩阵

(2)邻接表

[3. 图的遍历方式](#3. 图的遍历方式)

(1)深度优先搜索(dfs)

(2)广度优先搜索(bfs)

二、相关题目

[1. 所有可达路径](#1. 所有可达路径)

(1)邻接矩阵表示

(2)邻接表法

[2. 岛屿数量](#2. 岛屿数量)

(1)深度优先搜索

(2)广度优先搜索

[3. 岛屿的最大面积](#3. 岛屿的最大面积)

(1)深度优先搜索

(2)广度优先搜索

[4. 孤岛的总面积](#4. 孤岛的总面积)

[5. 沉没孤岛](#5. 沉没孤岛)

[6. 水流问题](#6. 水流问题)

[7. 建造最大岛屿](#7. 建造最大岛屿)

[8. 岛屿的周长](#8. 岛屿的周长)


一、基础知识

1. 图的种类

(1)有向图

(2)无向图

(3)加权有向图

2. 图的构造

(1)邻接矩阵

邻接矩阵 使用 二维数组来表示图结构。 邻接矩阵是从节点的角度来表示图,有多少节点就申请多大的二维数组。

例如:

grid[2][5] = 6,表示 节点 2 连接 节点 5 为 有向图,节点 2 指向 节点 5,边的 权值为 6。

如果想 表示 无向图,即:grid[2][5] = 6,grid[5][2] = 6,表示 节点 2 与 节点 5 相互连通,权值为6。

邻接矩阵的优点:
  • 表达方式 简单,易于 理解。
  • 检查 任意两个顶点间 是否 存在边的 操作 非常快。
  • 适合 稠密图,在边数 接近 顶点数 平方的图中,邻接矩阵 是一种 空间效率 较高的 表示方法。
缺点:
  • 遇到 稀疏图,会 导致申请 过大的 二维数组造成空间浪费 且遍历 边 的时候 需要 遍历整个 n * n 矩阵,造成 时间 浪费。

(2)邻接表

邻接表 使用 数组 + 链表的方式来 表示。 邻接表 是从 边的数量 来表示图,有多少边 才会申请 对应大小的 链表。

上图表示为

  • 节点 1 指向 节点 3 和 节点 5
  • 节点 2 指向 节点 4、节点 3、节点 5
  • 节点 3 指向 节点 4
  • 节点 4 指向 节点 1
邻接表的优点:
  • 对于 稀疏图的存储,只需要 存储边,空间 利用率高。
  • 遍历 节点 连接情况 相对容易。
缺点:
  • 检查任 意两个节点间 是否存在 边,效率 相对低,需要 O(V) 时间,V 表示某 节点 连接其他 节点的 数量。
  • 实现 相对复杂,不易 理解。

3. 图的遍历方式

(1)深度优先搜索(dfs)

cpp 复制代码
// 代码框架
void dfs(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本节点所连接的其他节点) {
        处理节点;
        dfs(图,选择的节点); // 递归
        回溯,撤销处理结果
    }
}

(2)广度优先搜索(bfs)

用一个方格地图,假如每次搜索的方向为 上下左右(不包含斜上方),那么给出一个start起始位置,那么BFS就是从四个方向走出第一步。

cpp 复制代码
// 代码框架
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {

    queue<pair<int, int>> que; // 定义队列
    que.push({x, y}); // 起始节点加入队列

    visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点

    while(!que.empty()) { // 开始遍历队列里的元素

        pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素

        int curx = cur.first;
        int cury = cur.second; // 当前节点坐标

        for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历

            int nextx = curx + dir[i][0];
            int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
            if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 坐标越界了,直接跳过

            if (!visited[nextx][nexty]) { // 如果节点没被访问过
                que.push({nextx, nexty});  // 队列添加该节点为下一轮要遍历的节点
                visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
            }
        }
    }
}

二、相关题目

1. 所有可达路径

98. 所有可达路径https://kamacoder.com/problempage.php?pid=1170

给定一个有 n 个节点的有向无环图,节点编号从 1 到 n。请编写一个函数,找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。

cpp 复制代码
输入描述
第一行包含两个整数 N,M,表示图中拥有 N 个节点,M 条边
后续 M 行,每行包含两个整数 s 和 t,表示图中的 s 节点与 t 节点中有一条路径

输出描述
输出所有的可达路径,路径中所有节点之间空格隔开,每条路径独占一行,存在多条路径,路径输出的顺序可任意。如果不存在任何一条路径,则输出 -1。
注意输出的序列中,最后一个节点后面没有空格! 例如正确的答案是 `1 3 5`,而不是 `1 3 5 `, 5后面没有空格!

输入示例

5 5
1 3
3 5
1 2
2 4
4 5

输出示例

1 3 5
1 2 4 5

(1)邻接矩阵表示

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;
 
vector<vector<int>> result;
vector<int> path;
 
void dfs (const vector<vector<int>>& graph, int x, int n) {
    if (x == n) {
        result.push_back(path);
        return ;
    }
    for (int i = 1; i <= n; i++) {
        // 找到 n 指向的节点
        if (graph[x][i] == 1) {
            path.push_back(i);
            dfs(graph, i, n);
            path.pop_back();
        }
    }
}
 
 
int main() {
     
    // 获取数据
    int n, m, s, t;
    cin >> n >> m;
     
    vector<vector<int>> graph(n + 1, vector<int>(n + 1, 0));
    while (m--) {
        cin >> s >> t;
        // s 指向 t
        graph[s][t] = 1;
    }
     
    // 都是从 1 开始
    path.push_back(1);
    dfs(graph, 1, n);   // 当前遍历节点 1
     
    // 打印
    if (result.size() == 0) cout << -1 << endl;
    for (const vector<int> &pa : result) {
        for (int i = 0; i < pa.size() - 1; i++) {
            cout << pa[i] << " ";
        }
        cout << pa[pa.size() - 1]  << endl;
    }
     
    return 0;
}

(2)邻接表法

cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
using namespace std;
 
vector<vector<int>> result;
vector<int> path;
 
void dfs (const vector<list<int>>& graph, int x, int n) {
    if (x == n) {
        result.push_back(path);
        return ;
    }
    for (int i : graph[x]) {
        // 找到 x 指向的链表
        path.push_back(i);
        dfs(graph, i, n);
        path.pop_back();
    }
}
 
 
int main() {
     
    // 邻接表解法
    // 获取数据
    int n, m, s, t;
    cin >> n >> m;
     
    vector<list<int>> graph(n + 1);
    while (m--) {
        cin >> s >> t;
        // s 指向 t
        graph[s].push_back(t);
    }
     
    // 都是从 1 开始
    path.push_back(1);
    dfs(graph, 1, n);   // 当前遍历节点 1
     
    // 打印
    if (result.size() == 0) cout << -1 << endl;
    for (const vector<int> &pa : result) {
        for (int i = 0; i < pa.size() - 1; i++) {
            cout << pa[i] << " ";
        }
        cout << pa[pa.size() - 1]  << endl;
    }
     
    return 0;
}

2. 岛屿数量

99. 岛屿数量https://kamacoder.com/problempage.php?pid=1171

给定一个由 1(陆地)和 0(水)组成的矩阵,你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

cpp 复制代码
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。
后续 N 行,每行包含 M 个数字,数字为 1 或者 0。

输出描述
输出一个整数,表示岛屿的数量。如果不存在岛屿,则输出 0。

输入示例

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例

3

思路:

用遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都标记上。

在遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。

(1)深度优先搜索

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;
 
int dir[4][2] = {0, -1, 0, 1, -1, 0, 1, 0}; // 记录上下左右坐标
void dfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
    for (int i = 0; i < 4; i++) {
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        // 超出范围直接跳过
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
     
        if (grid[nextx][nexty] == 1 && !visited[nextx][nexty]) {
            visited[nextx][nexty] = true;
            dfs(grid, visited, nextx, nexty);
        }
    }
}
 
int main() {
     
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
     
    // 记录是否为同一块陆地
    vector<vector<bool>> visited(n, vector<bool>(m, false));
     
    int result = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            // 如果遍历到的陆地没有被占,那么陆地的数量 +1
            // 并且将陆地上的所有板块全部标记
            if (grid[i][j] == 1 && !visited[i][j]) {
                result++;
                visited[i][j] = true;
                dfs(grid, visited, i, j);
            }
        }
    }
     
    cout << result << endl;
     
    return 0;
}

(2)广度优先搜索

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
 
int dir[4][2] = {0, -1, 0, 1, -1, 0, 1, 0}; // 记录上下左右坐标
void dfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
    // 广度优选算法
    queue<pair<int, int>> que;
    que.push({x, y});
    visited[x][y] = true;   // 加入队列后立即保存当前位置
    while (!que.empty()) {
        pair<int ,int> cur = que.front(); que.pop();
        int curx = cur.first;
        int cury = cur.second;
        for (int i = 0; i < 4; i++) {
            int nextx = dir[i][0] + curx;
            int nexty = dir[i][1] + cury;
            if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
             
            if (grid[nextx][nexty] == 1 && !visited[nextx][nexty]) {
                dfs(grid, visited, nextx, nexty);
            }
        }
    }
}
 
int main() {
     
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
     
    // 记录是否为同一块陆地
    vector<vector<bool>> visited(n, vector<bool>(m, false));
     
    int result = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            // 如果遍历到的陆地没有被占,那么陆地的数量 +1
            // 并且将陆地上的所有板块全部标记
            if (grid[i][j] == 1 && !visited[i][j]) {
                result++;
                visited[i][j] = true;
                dfs(grid, visited, i, j);
            }
        }
    }
     
    cout << result << endl;
     
    return 0;
}

3. 岛屿的最大面积

100. 岛屿的最大面积https://kamacoder.com/problempage.php?pid=1172

给定一个由 1(陆地)和 0(水)组成的矩阵,计算岛屿的最大面积。岛屿面积的计算方式为组成岛屿的陆地的总数。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。你可以假设矩阵外均被水包围。

cpp 复制代码
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。后续 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述
输出一个整数,表示岛屿的最大面积。如果不存在岛屿,则输出 0。

输入示例

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例

4

(1)深度优先搜索

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int count;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
    for (int i = 0; i < 4; i++) {
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 越界了,直接跳过
        if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 没有访问过的 同时 是陆地的
            visited[nextx][nexty] = true;
            count++;
            dfs(grid, visited, nextx, nexty);
        }
    }
}


int main() {
    
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    
    vector<vector<bool>> visited(n, vector<bool>(m, false));
    
    int result = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (!visited[i][j] && grid[i][j] == 1) {
                // dfs处理下一个节点,所以count初值为0
                count = 1;
                visited[i][j] = true;
                dfs(grid, visited, i, j);
                result = max(result, count);
            }
        }
    }
    
    cout << result << endl;
    
    return 0;
}

(2)广度优先搜索

cpp 复制代码
class Solution {
private:
    int count;
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
    void bfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
        queue<int> que;
        que.push(x);
        que.push(y);
        visited[x][y] = true; // 加入队列就意味节点是陆地可到达的点
        count++;
        while(!que.empty()) {
            int xx = que.front();que.pop();
            int yy = que.front();que.pop();
            for (int i = 0 ;i < 4; i++) {
                int nextx = xx + dir[i][0];
                int nexty = yy + dir[i][1];
                if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界
                if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 节点没有被访问过且是陆地
                    visited[nextx][nexty] = true;
                    count++;
                    que.push(nextx);
                    que.push(nexty);
                }
            }
        }
    }

public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));
        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == 1) {
                    count = 0;
                    bfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                    result = max(result, count);
                }
            }
        }
        return result;
    }
};

4. 孤岛的总面积

101. 孤岛的总面积https://kamacoder.com/problempage.php?pid=1173

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。

现在你需要计算所有孤岛的总面积,岛屿面积的计算方式为组成岛屿的陆地的总数。

cpp 复制代码
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0。

输出描述
输出一个整数,表示所有孤岛的总面积,如果不存在孤岛,则输出 0。

输入示例

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例

1

思路:

在遇到地图周边陆地的时候,将1都变为0,此时地图为这样:

然后我们再去遍历这个地图,遇到有陆地的地方,去采用深搜或者广搜,边统计所有陆地。

cpp 复制代码
// 深度优先搜索
#include <iostream>
#include <vector>
using namespace std;
 
int count;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void dfs(vector<vector<int>>& grid, int x, int y) {
    for (int i = 0; i < 4; i++) {
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
        if (grid[nextx][nexty] == 1) {
            grid[nextx][nexty] = 0;
            dfs(grid, nextx, nexty);
        }
    }
}
 
int main() {
     
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n , vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
     
    int result = 0;
     
    // 左右两边岛屿
    for (int i = 0; i < n; i++) {
        if (grid[i][0] == 1) dfs(grid, i, 0);
        if (grid[i][m - 1] == 1) dfs(grid, i, m - 1);
    }
     
    // 上下两边岛屿
    for (int i = 0; i < m; i++) {
        if (grid[0][i] == 1) dfs(grid, 0, i);
        if (grid[n - 1][i] == 1) dfs(grid, n - 1, i);
    }
     
    // 计算孤岛总面积
    for (int i = 1; i < n - 1; i++) {
        for (int j = 1; j < m - 1; j++) {
            if (grid[i][j] == 1) result++;
        }
    }
     
    cout << result << endl;
     
    return 0;
}

5. 沉没孤岛

102. 沉没孤岛https://kamacoder.com/problempage.php?pid=1174

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。

现在你需要将所有孤岛"沉没",即将孤岛中的所有陆地单元格(1)转变为水域单元格(0)。

cpp 复制代码
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。
之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述
输出将孤岛"沉没"之后的岛屿矩阵。 注意:每个元素后面都有一个空格

输入示例

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例

1 1 0 0 0
1 1 0 0 0
0 0 0 0 0
0 0 0 1 1

思路:

定义一个 visited 二维数组,单独标记 周边的陆地,然后 遍历地图 的时候同时对 数组 board 和 数组 visited 进行判断,决定 陆地是否变成水域。

  • 步骤一:深搜或者广搜将地图周边的 1 (陆地)全部改成 2 (特殊标记)
  • 步骤二:将水域中间 1 (陆地)全部改成 水域(0)
  • 步骤三:将之前标记的 2 改为 1 (陆地)
cpp 复制代码
// 深度优先搜索
#include <iostream>
#include <vector>
using namespace std;
 
int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
void dfs(vector<vector<int>>& grid, int x, int y) {
    grid[x][y] = 2;
    for (int i = 0; i < 4; i++) { // 向四个方向遍历
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        // 超过边界
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
        // 不符合条件,不继续遍历
        if (grid[nextx][nexty] == 0 || grid[nextx][nexty] == 2) continue;
        dfs (grid, nextx, nexty);
    }
    return;
}
 
int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
 
    // 步骤一:
    // 从左侧边和右侧边 向中间遍历
    for (int i = 0; i < n; i++) {
        if (grid[i][0] == 1) dfs(grid, i, 0);
        if (grid[i][m - 1] == 1) dfs(grid, i, m - 1);
    }
 
    // 从上边和下边向中间遍历
    for (int j = 0; j < m; j++) {
        if (grid[0][j] == 1) dfs(grid, 0, j);
        if (grid[n - 1][j] == 1) dfs(grid, n - 1, j);
    }
     
    // 步骤二、步骤三
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) grid[i][j] = 0;
            if (grid[i][j] == 2) grid[i][j] = 1;
        }
    }
     
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cout << grid[i][j] << " ";
        }
        cout << endl;
    }
}

6. 水流问题

103. 水流问题https://kamacoder.com/problempage.php?pid=1175

现有一个 N × M 的矩阵,每个单元格包含一个数值,这个数值代表该位置的相对高度。矩阵的左边界和上边界被认为是第一组边界,而矩阵的右边界和下边界被视为第二组边界。

矩阵模拟了一个地形,当雨水落在上面时,水会根据地形的倾斜向低处流动,但只能从较高或等高的地点流向较低或等高并且相邻(上下左右方向)的地点。我们的目标是确定那些单元格,从这些单元格出发的水可以达到第一组边界和第二组边界。

cpp 复制代码
输入描述
第一行包含两个整数 N 和 M,分别表示矩阵的行数和列数。 
后续 N 行,每行包含 M 个整数,表示矩阵中的每个单元格的高度。

输出描述
输出共有多行,每行输出两个整数,用一个空格隔开,表示可达第一组边界和第二组边界的单元格的坐标,输出顺序任意。

输入示例

5 5
1 3 1 2 4
1 2 1 3 2
2 4 7 2 1
4 5 6 1 1
1 4 1 2 1

输出示例

0 4
1 3
2 2
3 0
3 1
3 2
4 0
4 1

思路:

遍历每个点,然后看这个点 能不能同时到达第一组边界和第二组边界。

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
void dfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
    if (visited[x][y]) return;
    visited[x][y] = true;
    for (int i = 0; i < 4; i++) {
        int nextx = dir[i][0] + x;
        int nexty = dir[i][1] + y;
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
        if (grid[x][y] > grid[nextx][nexty]) continue;
        dfs(grid, visited, nextx, nexty);
    }
}

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    
    vector<vector<bool>> visitedleft(n, vector<bool>(m, false));
    vector<vector<bool>> visitedright(n, vector<bool>(m, false));
    
    // 上下
    for (int i = 0; i < m; i++) {
        dfs(grid, visitedleft, 0, i);
        dfs(grid, visitedright, n - 1, i);
    }
    
    // 左右
    for (int i = 0; i < n; i++) {
        dfs(grid, visitedleft, i, 0);
        dfs(grid, visitedright, i, m - 1);
    }
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (visitedleft[i][j] && visitedright[i][j]) {
                cout << i << " " << j << endl;
            }
        }
    }
    return 0;
}

7. 建造最大岛屿

104. 建造最大岛屿https://kamacoder.com/problempage.php?pid=1176

给定一个由 1(陆地)和 0(水)组成的矩阵,你最多可以将矩阵中的一格水变为一块陆地,在执行了此操作之后,矩阵中最大的岛屿面积是多少。

岛屿面积的计算方式为组成岛屿的陆地的总数。岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设矩阵外均被水包围。

cpp 复制代码
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述
输出一个整数,表示最大的岛屿面积。

输入示例

4 5
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1

输出示例

6

思路:

第一步:一次遍历地图,得出 各个岛屿的面积,并做编号记录。可以使用 map 记录,key 为岛屿编号,value 为岛屿面积

第二步:再遍历地图,遍历 0 的方格(因为要将 0 变成 1),并统计该 1(由 0 变成的 1)周边岛屿面积,将其 相邻面积 相加在一起,遍历所有 0 之后,就可以得出 选一个 0 变成 1 之后的最大面积。

cpp 复制代码
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
using namespace std;


int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int count;
void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y, int sign) {
    if (visited[x][y] || grid[x][y] == 0) return;
    visited[x][y] = true;
    grid[x][y] = sign;
    count++;
    for (int i = 0; i < 4; i++) {
        int nextx = dir[i][0] + x;
        int nexty = dir[i][1] + y;
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
        dfs(grid, visited, nextx, nexty, sign);
    }
}

int main() {
    
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    
    vector<vector<bool>> visited(n, vector<bool>(m, false));
    
    unordered_map<int, int> gridNum;
    int signals = 2;
    bool isAllGrid = true;
    // 为每一个岛屿编号,并记录对应编号岛屿的面积
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 0) isAllGrid = false;
            if (!visited[i][j] && grid[i][j] == 1) {
                count = 0;
                dfs(grid, visited, i, j, signals);
                gridNum[signals] = count;
                signals++;
            }
        }
    }
    
    // 全部为陆地的情况
    if (isAllGrid == true) {
        cout << n * m << endl;
        return 0;
    }
    
    // 遍历海洋变为陆地
    unordered_set<int> visitedGrid; // 记录加过的岛屿
    int counts = 0;
    int result = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 0) {
                counts = 1;
                visitedGrid.clear();
                for (int k = 0; k < 4; k++) {
                    int curx = i + dir[k][0];
                    int cury = j + dir[k][1];
                    if (curx < 0 || curx >= grid.size() || cury < 0 || cury >= grid[0].size()) continue;
                    if (visitedGrid.count(grid[curx][cury])) continue;
                    counts += gridNum[grid[curx][cury]];
                    visitedGrid.insert(grid[curx][cury]);
                }
                result = max(result, counts);
            }
        }
    }
    
    cout << result << endl;
    
    return 0;
}

8. 岛屿的周长

106. 岛屿的周长https://kamacoder.com/problempage.php?pid=1178

给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。

你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿,假设组成岛屿的陆地边长都为 1,请计算岛屿的周长。岛屿内部没有水域。

cpp 复制代码
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述
输出一个整数,表示岛屿的周长。

输入示例

5 5
0 0 0 0 0 
0 1 0 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0

输出示例

14
cpp 复制代码
// 写法一:
#include <iostream>
#include <vector>
using namespace std;
 
int count = 0;
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void bfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
    if (grid[x][y] == 0) {
        return;
    }
    for (int i = 0; i < 4; i++) {
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()
           || grid[nextx][nexty] == 0) {
            count++;
            continue;
        }
        if (!visited[nextx][nexty]) {
            visited[nextx][nexty] = true;
            bfs(grid, visited, nextx, nexty);
        }
    }
}
 
int main() {
     
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
     
    vector<vector<bool>> visited(n, vector<bool>(m, false));
     
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (!visited[i][j] && grid[i][j] == 1) {
                visited[i][j] = true;
                bfs(grid, visited, i, j);
            }
        }
    }
     
    cout << count << endl;
     
    return 0;
}


// 写法二:
#include <iostream>
#include <vector>
using namespace std;
 
 
int main() {
     
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
     
    int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
    int result = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                for (int k = 0; k < 4; k++) {
                    int x = i + direction[k][0];
                    int y = j + direction[k][1];
                    if (x < 0 || x >= grid.size()
                     || y < 0 || y >= grid[0].size()
                     || grid[x][y] == 0) {
                        result++;
                    }
                }
            }
        }
    }
     
    cout << result << endl;
     
    return 0;
}
相关推荐
亦枫Leonlew16 分钟前
微积分复习笔记 Calculus Volume 2 - 5.1 Sequences
笔记·数学·微积分
爱码小白1 小时前
网络编程(王铭东老师)笔记
服务器·网络·笔记
CYBEREXP20081 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos1 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习1 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
LuH11241 小时前
【论文阅读笔记】Learning to sample
论文阅读·笔记·图形渲染·点云
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
tianmu_sama2 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
chengooooooo2 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展