文章链接: 110. 字符串接龙 105.有向图的完全可达性 106. 岛屿的周长

题目链接: 110. 字符串接龙 105.有向图的完全可达性 106. 岛屿的周长


110.字符串接龙

思路:

第一步:判断点与点之间的关系,如果两个字符只差一个字母,那就是有链接。

第二步:求起点和终点的最短路径长度,即无向图求最短路。(广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径

#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <queue>
using namespace std;

int main() {
    string beginStr, endStr, str;
    int n; cin >> n;
    cin >> beginStr >> endStr;
    unordered_set<string> strSet; // 集合
    for (int i = 0; i < n; i++) { // 去重
        cin >> str;
        strSet.insert(str);
    }
    
    unordered_map<string, int> visitMap; // 记录字符串是否被访问过,同时记录路径长度
    visitMap.insert(pair<string, int>(beginStr, 1)); // 初始化(开始字符串也算为路径)
    
    queue<string> que;
    que.push(beginStr); // 初始化队列
    
    while (!que.empty()) {
        string word = que.front();
        que.pop();
        int path = visitMap[word]; // 找到这个字符串的路径长度
        
        for (int i = 0; i < word.size(); i++) {
            string newWord = word; // 因为之后的操作会改变word;
            
            for (int j = 0; j < 26; j++) {
                newWord[i] = j + 'a';
                if (newWord == endStr) { // 找到路径
                    cout << path + 1 << '\n';
                    return 0;
                }
                
                if (strSet.find(newWord) != strSet.end()
                && visitMap.find(newWord) == visitMap.end()) {
                    visitMap.insert(pair<string, int>(newWord, path + 1)); // 表示访问过的标记
                    que.push(newWord); // 继续BFS
                }
            }
        }
    }
    cout << 0 << '\n'; //  没找到
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.


105.有向图的完全可达性

思路:

本题是一个有向图搜索全路径的问题

用 DFS 和 BFS 都可以


深搜三部曲:

1.确认递归函数,参数

用 key 记录当前可以到达的点;  

用数组 visited 记录访问过的房间(默认数组里元素都是false,把元素标记为true就是处理 本节点了)。

void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited)
  • 1.

2.确定终止条件

写法一处理当前访问的节点,当前访问的节点如果是 true ,说明是访问过的节点,那就终止本层递归;如果不是true,我们就把它赋值为true,因为这是我们处理本层递归的节点。

void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
    if (visited[key]) {
        return;
    }
    visited[key] = true;
    list<int> keys = graph[key];
    for (int key : keys) {
        // 深度优先搜索遍历
        dfs(graph, key, visited);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

写法二处理下一层访问的节点,而不是当前层。那么就要在 深搜三部曲中第三步:处理目前搜索节点出发的路径的时候对 节点进行处理。

这样的话,就不需要终止条件,而是在 搜索下一个节点的时候,直接判断 下一个节点是否是我们要搜的节点。

void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
    list<int> keys = graph[key];
    for (int key : keys) {
        if (visited[key] == false) { // 确认下一个是没访问过的节点
            visited[key] = true;
            dfs(graph, key, visited);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

3.处理目前搜索节点出发的路径

本题不需要用到回溯的原因?

本题是需要判断1节点是否能到所有节点,只要将所有遍历过的节点作上标记即可,不用恢复现场(回溯)。

那么什么时候需要恢复现场

当我们需要搜索一条可行路径的时候,就需要回溯操作,因为只有恢复了现场,才能知道所有可行的路径。而不是因为某个现场被破坏而少走了几条符合条件的可行路径。

注意:回溯是为了恢复现场,为了将用过的条件恢复,再去走下一条路,从而去得出一条符合条件的路。也就是说,只有条件受到影响并且影响到了结果的求解时才回溯。

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

void dfs(const vector<list<int>>& graph, int key, vector<bool>& visited) {
    if (visited[key]) {
        return;
    }
    visited[key] = true; // 记录访问过的点
    list<int> keys = graph[key];
    for (int key : keys) { 
        dfs(graph, key, visited); 
    }
}

int main() {
    int n, m, s, t;
    cin >> n >> m;
    
    vector<list<int>> graph(n + 1); // 邻接表
    while(m--) {
        cin >> s >> t;
        graph[s].push_back(t);
    }
    vector<bool> visited(n + 1, false); // 记录访问过得到点
    dfs(graph, 1, visited);
    // 最后判断是否全部都遍历到了
    for (int i = 1; i <= n; i++) {
        if (visited[i] == false) {
            cout << -1 << '\n';
            return 0;
        }
    }
    cout << 1 << '\n';
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.


106.岛屿的周长

思路:

解法一

遍历每一个空格,遇到岛屿则计算其上下左右的空格情况。

#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 dir[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 + dir[k][0];
                    int y = j + dir[k][1];
                    if (x < 0 // 左边界
                        || x >= grid.size() // 右边界
                        || y < 0 // 上边界
                        || y >= grid[0].size() // 下边界
                        || grid[x][y] == 0) { // 水域
                            result++;
                        }
                }
            }
        }
    }
    cout << result << '\n';
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

解法二

结果 result = 岛屿数量 * 4 - cover * 2。(相邻岛屿数量为cover)

#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 sum = 0;    // 陆地数量
    int cover = 0;  // 相邻数量
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                sum++; // 统计总的陆地数量
                // 统计上边相邻陆地
                if(i - 1 >= 0 && grid[i - 1][j] == 1) cover++;
                // 统计左边相邻陆地
                if(j - 1 >= 0 && grid[i][j - 1] == 1) cover++;
                // 为什么没统计下边和右边? 因为避免重复计算
            }
        }
    }

    cout << sum * 4 - cover * 2 << endl;

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.