代码随想录算法训练营第46天|106. 岛屿的周长、110. 字符串接龙、105.有向图的完全可达性

卡玛网106

2025-03-21 19:12:25 星期五

题目描述:卡玛网106
文档讲解:代码随想录(programmercarl)106. 岛屿的周长

梳理

  1. 首先是需要遍历每一个图中的岛屿(也就是1)

  2. 对每一个为1的岛屿遍历其四周是否有海洋或者与边界连接(这种情况也算)

  3. 最后返回count

卡玛网测试

这里其实本题有几个坑需要记录一下

  1. 刚看了一下文档讲解,首先第一个本题k哥说是为了避免惯性思维,其实压根用不着dfs或者bfs,结果还是用了dfs

  2. 就是在深搜的函数中,写的calPerimeter函数(这个其实是从104来的思路)是对nextx和nexty计算的,结果结果一直不对,后来把所有的点打印出来对照看了一下,发现才是没有对第一个点(陆地)的周边进行统计,如下图

  1. 另外一个坑就是属于没有想到的,就是像这种挨着边界的陆地,我一开始在calPerimeter是没有做判断的,也就是边界情况也需要进行count++。

同时,在进行判断的时候需要注意,这里需要加一个else if的判断,否则grid[nearx][neary]会出现数组越界的情况。


if (nearx < 0 || nearx >= grid.size() || neary < 0 || neary >= grid[0].size()) count++;
else if (grid[nearx][neary] == 0) {
	// cout << nearx << ' ' << neary << endl;
	count++;
}

dfs代码

这个其实完全没必要,下面的精简版就是直接删了dfs的

点击查看代码
#include<iostream>
#include<vector>
using namespace std;
int count = 0;
int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};


void calPerimeter(vector<vector<int>> &grid, vector<vector<int>> &visited, int x, int y) {
    for (int i = 0; i < 4; i++) {
        int nearx = x + dir[i][0];
        int neary = y + dir[i][1];
        if (nearx < 0 || nearx >= grid.size() || neary < 0 || neary >= grid[0].size()) count++;
        else if (grid[nearx][neary] == 0) {
            // cout << nearx << ' ' << neary << endl;
            count++;
        }
    }
}

void dfs(vector<vector<int>> &grid, vector<vector<int>> &visited, int x, int y) {
    if (grid[x][y] == 0 || visited[x][y] == 1) return;
    visited[x][y] = 1;
    calPerimeter(grid, visited, x, 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] == 0) {
            // calPerimeter(grid, 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));
    vector<vector<int>> visited(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++) {
        for (int j = 0; j < M; j++) {
            if (grid[i][j] == 1 && visited[i][j] == 0) {
                dfs(grid, visited, i, j);
            } 
        }
    }

    cout << count << endl;
}

精简版本代码

点击查看代码
#include<iostream>
#include<vector>
using namespace std;
int count = 0;
int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};


void calPerimeter(vector<vector<int>> &grid, int x, int y) {
    for (int i = 0; i < 4; i++) {
        int nearx = x + dir[i][0];
        int neary = y + dir[i][1];
        if (nearx < 0 || nearx >= grid.size() || neary < 0 || neary >= grid[0].size()) count++;
        else if (grid[nearx][neary] == 0) {
            // cout << nearx << ' ' << neary << endl;
            count++;
        }
    }
}

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++) {
        for (int j = 0; j < M; j++) {
            if (grid[i][j] == 1) {
                calPerimeter(grid, i, j);
            } 
        }
    }

    cout << count << endl;
}

卡玛网110

题目描述:卡玛网110
文档讲解:代码随想录(programmercarl)110. 字符串接龙

梳理

  1. 首先需要对字符串进行存储。这里每一个字符串直接用cin即可

    因为本题要求strList里的每个字符串只用使用一次,所以还需要定义一个unordered_set类型的strSet表示其中的字符串是否被访问过

  2. 本题中使用广搜,对字符串进行搜索,在此过程中进行的操作是不断对字符串的某一位进行替换,而不是像之前岛屿问题那样计算相邻的陆地然后进行标记。最后就是要看结果是否等于endStr。

    这里替换的操纵就是用两个for循环,i遍历字符串的长度,j遍历26个字母,newWord[i] = j + 'a'

  3. 至于搜索的路径是不是最短路径,这里有说法

在无权图中,用广搜求最短路最为合适,广搜只要搜到了终点,那么一定是最短的路径

因为广搜就是以起点中心向四周扩散的搜索。

  1. 所以搜索方式就需要使用广搜来进行,同时因为最后输出的结果是一个路径的长度,需要在搜索的过程中不断记录当前现有路径的长度。这里就需要用到一个unordered_map<pair<string, int>>来记录每一个字符串的路径长度值

卡玛网测试

这个题有一个小细节需要注意,一直报错找了好久。我刚开始把newWord放在了箭头所指的位置,代码是下面的部分,就是会导致一个很隐蔽的问题,就是一旦在箭头所指的位置定义了newWord,那么他就无法保证每次只修改一位字符了。可能会修改很多位

如果放在正常的位置每次是只会修改一位,就不会改了前面的,还把后面的也改了。



while(!que.empty()) {
	string word = que.front();
	que.pop();

	int path = visitMap[word];

	for (int i = 0; i < newWord.size(); i++) {
		string newWord = word;

		for (int j = 0; j < 26; j++) {
点击查看代码
#include<iostream>
#include<vector>
#include<queue>
#include<unordered_map>
#include<unordered_set>
using namespace std;
int main() {
    int N;
    string str;
    string startStr, endStr;

    cin >> N;
    cin >> startStr >> endStr;
    unordered_set<string> strSet;
    for (int i = 0; i < N; i++) {
        cin >> str;
        strSet.insert(str);
    }

    queue<string> que;
    que.push(startStr);

    unordered_map<string, int> visitMap;
    visitMap.insert(pair<string, int>(startStr, 1));


    while(!que.empty()) {
        string word = que.front();
        que.pop();
        
        int path = visitMap[word];

        for (int i = 0; i < word.size(); i++) {
            string newWord = word;
            // cout << newWord << endl;
            for (int j = 0; j < 26; j++) {
                newWord[i] = j + 'a';
                if (newWord == endStr) {
                    cout << path + 1 << endl;
                    return 0;
                }
                else {
                    if (strSet.count(newWord) == 1 && visitMap.count(newWord) == 0) {
                        visitMap.insert(pair<string, int>(newWord, path + 1));
                        que.push(newWord);
                    } 
                }
            }
        }
    }

    cout << 0 << endl;

}

卡玛网105

题目描述:卡码网105
文档讲解:代码随想录(programmercarl)105.有向图的完全可达性

本题需要使用到邻接表的存储,核心就是用深搜或者广搜遍历一个有向图。在dfs中,k哥强调的地方是分两种情况,讨论处理当前结点处理下一个结点

梳理

  1. 首先使用邻接表存储一个图。邻接表的使用需要用到vector<list>这种类型,我们定义一个vector<list<int>> graph(N + 1);

  2. 之后使用深度优先遍历,对第一个结点指向的结点进行逐个遍历,处理的部分就是将其的visited数组标记为1即可。

    需要注意的是,使用邻接表存储的图在dfs的参数有变化,分别是void dfs(vector<list<int>> graph, int key, vector<int> &visited)

  3. 如果最后visited数组所有的值的标记都是1了,那么就说明第一个结点可达所有的结点。

卡玛网测试

本题需要注意的一点就是,因为结点是从1开始记录的,那么申请一个数组的时候,就需要开辟(N + 1)的空间。举个例子,我们要存储[1, 2, 3, 4],那么开辟大小为5的数组,同时为了保证只存储4个,从i = 1开始遍历即可,如下。


for (int i = 1; i < visited.size(); i++) {
		if (visited[i] == 0) {
			cout << -1 << endl;
			return 0;
		}
	}

剩下的代码思路比较好理解

处理当前结点

点击查看代码
#include<iostream>
#include<vector>
#include<list>
using namespace std;

void dfs(vector<list<int>> graph, int key, vector<int> &visited) {
    if (visited[key] == 1) return;
    visited[key] = 1;
    list<int> keys = graph[key];
    for (int key : keys) {
        dfs(graph, key, visited);
    }
}

int main() {
    int N, K;
    int s, t;
    cin >> N >> K;
    vector<list<int>> graph(N + 1);
    // 图的存储
    for (int i = 0; i < K; i++) { // 表示存储几条边,下标从0或者1开始都无所谓
        cin >> s >> t;
        graph[s].push_back(t);
    }

    vector<int> visited(N + 1, 0);
    dfs(graph, 1, visited);

    for (int i = 1; i < visited.size(); i++) {
        if (visited[i] == 0) {
            cout << -1 << endl;
            return 0;
        }
    }
    cout << 1 << endl;
    
}

处理下一个结点

这个和之前99岛屿数量两个版本的深搜是一致的。

点击查看代码
#include<iostream>
#include<vector>
#include<list>
using namespace std;

void dfs(vector<list<int>> graph, int key, vector<int> &visited) {
    list<int> keys = graph[key];
    for (int key : keys) {
        if (visited[key] == 0) {
            visited[key] = 1;
            dfs(graph, key, visited);
        }   
    }
}

int main() {
    int N, K;
    int s, t;
    cin >> N >> K;
    vector<list<int>> graph(N + 1);
    // 图的存储
    for (int i = 0; i < K; i++) { // 表示存储几条边,下标从0或者1开始都无所谓
        cin >> s >> t;
        graph[s].push_back(t);
    }

    vector<int> visited(N + 1, 0);
    visited[1] = 1;
    dfs(graph, 1, visited);

    for (int i = 1; i < visited.size(); i++) {
        if (visited[i] == 0) {
            cout << -1 << endl;
            return 0;
        }
    }
    cout << 1 << endl;
    
}
posted on 2025-03-21 19:13  bnbncch  阅读(20)  评论(0)    收藏  举报