算法日记 43-44 day 图论(深搜,广搜)

直接看题目,还是熟悉写法。

题目:孤岛的总面积

101. 孤岛的总面积 (kamacoder.com)

题目描述

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

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

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0。

输出描述

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

题目分析:

题目求得孤岛,孤岛不和边界相接。那我们把边界上的陆地变成海洋,剩下得不就只有孤岛了吗?

来看看具体得写法:

//深搜

cpp 复制代码
//深搜
#include<iostream>
#include<vector>
 
using namespace std;
 
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int count;
 
void DFS(vector<vector<int>>& grid,int x,int y){
    grid[x][y]=0;//把陆地变成海洋
    count++;
    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) 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 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);
    }
     
    count=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(grid[i][j]==1){
                DFS(grid,i,j);
            }
        }
    }
    std::cout << count << std::endl;
}

//广搜

cpp 复制代码
//广搜
#include<iostream>
#include<vector>
#include<queue>

using namespace std;

int dir[4][2]={1,0,0,1,-1,0,0,-1};
int count;

void BFS(vector<vector<int>>& grid,int x,int y){
    queue<pair<int,int>> que;
    que.push({x,y});
    
    grid[x][y]=0;//把陆地变成海洋
    count++;
    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 (grid[nextX][nextY] == 1){
                que.push({nextX,nextY});
                count++;
                grid[nextX][nextY]=0;
            }
        }
    }
    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) BFS(grid,i,0);
        if(grid[i][m-1]==1) BFS(grid,i,m-1);
    }
    for(int i=0;i<m;i++){
        if(grid[0][i]==1) BFS(grid,0,i);
        if(grid[n-1][i]==1) BFS(grid,n-1,i);
    }
    
    count=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(grid[i][j]==1){
                BFS(grid,i,j);
            }
        }
    }
    std::cout << count << std::endl;
}

题目:沉没孤岛

102. 沉没孤岛 (kamacoder.com)

题目描述

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

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

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。

之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述

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

题目分析:

上一题中我们知道了怎么去寻找孤岛,那么找到之后把他变成0不久好了吗?不过上一题中我们把陆地变成海洋,现在不行了,因为陆地时不能变得。那么我们在遍历的时候把这些陆地给标记一下,孤岛沉没之后变回来就好了。这个标记呢,可以使用二维数组,也可以把1变成2,能和海洋区分就可以。

//深搜

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 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=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++){
            std::cout << grid[i][j] << " ";
        }
        std::cout << std::endl;
    }
}

//广搜

cpp 复制代码
#include<iostream>
#include<vector>
#include<queue>
 
using namespace std;
 
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int count;
 
void BFS(vector<vector<int>>& grid,int x,int y){
    queue<pair<int,int>> que;
    que.push({x,y});
     
    grid[x][y]=2;//把陆地变成海洋
    count++;
    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 (grid[nextX][nextY] == 1){
                que.push({nextX,nextY});
                count++;
                grid[nextX][nextY]=2;
            }
        }
    }
    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) BFS(grid,i,0);
        if(grid[i][m-1]==1) BFS(grid,i,m-1);
    }
    for(int i=0;i<m;i++){
        if(grid[0][i]==1) BFS(grid,0,i);
        if(grid[n-1][i]==1) BFS(grid,n-1,i);
    }
     
    count=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(grid[i][j]==1){
                grid[i][j]=0;//孤岛变为0
            }
            if(grid[i][j]==2){
                grid[i][j]=1;//陆地还原
            }
        }
    }
     
     
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            std::cout << grid[i][j]<<" ";
        }
        std::cout <<std::endl;
    }
}

题目:水流问题

103. 水流问题 (kamacoder.com)

题目描述

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

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

输入描述

第一行包含两个整数 N 和 M,分别表示矩阵的行数和列数。

后续 N 行,每行包含 M 个整数,表示矩阵中的每个单元格的高度。

输出描述

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

题目分析:

判断高出的水能不能流到边界。一种就是直接去搜索能否到达边界,另外一种,反过来,边界出逆流而上能到达哪些区域。下面的题我使用了第二种方式。

深搜

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,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=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[x][y]>grid[nextX][nextY]) continue;
        DFS(grid,visited,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];
        }
    }
    
    // 标记从第一组边界上的节点出发,可以遍历的节点
    vector<vector<bool>> firstBorder(n, vector<bool>(m, false));

    // 标记从第一组边界上的节点出发,可以遍历的节点
    vector<vector<bool>> secondBorder(n, vector<bool>(m, false));
    
    for(int i=0;i<n;i++){
        DFS(grid,firstBorder,i,0);
        DFS(grid,secondBorder,i,m-1);
    }
    for(int j=0;j<m;j++){
        DFS(grid,firstBorder,0,j);
        DFS(grid,secondBorder,n-1,j);
    }
    
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            // 如果这个节点,从第一组边界和第二组边界出发都遍历过,就是结果
            if (firstBorder[i][j] && secondBorder[i][j]) cout << i << " " << j << endl;
        }
    }

题目:建造最大的岛屿

104. 建造最大岛屿 (kamacoder.com)

题目描述

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

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

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述

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

题目分析:

把海洋变成陆地,让岛屿面积最大。最直观的解法就是遍历每一个海洋区域,把他变成陆地后,搜索最大的陆地面积。但是这样的搜索会让我重复大量的无用操作,这个操作主要在搜索岛屿的问题上,那么减少搜索就可以完成优化。

首先把所有的陆地面积都记录下来,并且编号,等到遍历海洋区域的时候,我们直接加上这一部分的面积,就可以了,避免了重复搜索。

//深搜

cpp 复制代码
#include<iostream>
#include<vector>
#include <unordered_set>
#include <unordered_map>

using namespace std;
int n,m;
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int count;

void DFS(vector<vector<int>>& gird,vector<vector<bool>>& visited,int x,int y,int mark){
    if(gird[x][y]==0||visited[x][y]) return;
    gird[x][y]=mark;//定义编号
    visited[x][y]=true;
    count++;
    for(int i=0;i<4;i++){
        int nextx=x+dir[i][0];
        int nexty=y+dir[i][1];
        if(nextx<0||nextx>=n||nexty<0||nexty>=m) continue;
        DFS(gird,visited,nextx,nexty,mark);
    }
}


int main(){

    cin>>n>>m;
    vector<vector<int>> gird(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>gird[i][j];
        }
    }
    //标记那些格子访问过
    vector<vector<bool>> visited(n,vector<bool>(m,false));
    unordered_map<int ,int> gridNum; //记录陆地大小
    int mark = 2; // 记录每个岛屿的编号
    bool isAllGrid = true; // 标记是否整个地图都是陆地
    
    
    //先统计每快陆地的大小
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(gird[i][j]==0) isAllGrid=false;
            if(gird[i][j]==1&&!visited[i][j]){
                count=0;
                DFS(gird,visited,i,j,mark);
                gridNum[mark]=count;//记录大小
                mark++;
            }
        }
    }
    
    if(isAllGrid){
        cout<<n*m<<endl;
        return 0;
    }
     // 以下逻辑是根据添加陆地的位置,计算周边岛屿面积之和
    int result = 0; // 记录最后结果
    unordered_set<int> visitedGrid; // 标记访问过的岛屿
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            visitedGrid.clear();
            count = 1; // 记录连接之后的岛屿数量
            if(gird[i][j]==0){
                for(int k=0;k<4;k++){
                    int neari = i + dir[k][1]; // 计算相邻坐标
                    int nearj = j + dir[k][0];
                    if (neari < 0 || neari >= n || nearj < 0 || nearj >= m) continue;
                    //count查找是否存在
                    if(visitedGrid.count(gird[neari][nearj])) continue;//添加过的岛屿不添加
                    // 把相邻四面的岛屿数量加起来
                    count += gridNum[gird[neari][nearj]];
                    visitedGrid.insert(gird[neari][nearj]); // 标记该岛屿已经添加过
                }
            }
            result=max(result,count);
        }
    }
    cout<<result<<endl;
}

题目:字符串接龙

110. 字符串接龙 (kamacoder.com)

题目描述

字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列:

  1. 序列中第一个字符串是 beginStr。

  2. 序列中最后一个字符串是 endStr。

  3. 每次转换只能改变一个字符。

  4. 转换过程中的中间字符串必须是字典 strList 中的字符串,且strList里的每个字符串只用使用一次。

给你两个字符串 beginStr 和 endStr 和一个字典 strList,找到从 beginStr 到 endStr 的最短转换序列中的字符串数目。如果不存在这样的转换序列,返回 0。

输入描述

第一行包含一个整数 N,表示字典 strList 中的字符串数量。 第二行包含两个字符串,用空格隔开,分别代表 beginStr 和 endStr。 后续 N 行,每行一个字符串,代表 strList 中的字符串。

输出描述

输出一个整数,代表从 beginStr 转换到 endStr 需要的最短转换序列中的字符串数量。如果不存在这样的转换序列,则输出 0。

输入示例

6
abc def
efc
dbc
ebc
dec
dfc
yhn

输出示例

4

提示信息

从 startStr 到 endStr,在 strList 中最短的路径为 abc -> dbc -> dec -> def,所以输出结果为 4,如图:

数据范围:

2 <= N <= 500

题目分析:

实际上就是一个求最短路径,关键在于我们如何去找下一个结点。显然,将原来的字符串的字符改变就可以得到下一个结点了,而这个新的字符串如果在我们的字典中,那就意味着可行。本题使用广搜即可,深搜的话很麻烦,需要处理回溯。

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

int main(){
    int n;
    string beginStr,endStr,str;
    cin>>n;
    cin>>beginStr>>endStr;
    
    unordered_set<string> strSet;
    for(int i=0;i<n;i++){
        cin >> str;
        strSet.insert(str);
    }
    
    
    // 记录strSet里的字符串是否被访问过,同时记录路径长度
    unordered_map<string, int> visitMap; // <记录的字符串,路径长度>
    
    queue<string> que;
    que.push(beginStr);
    visitMap.insert(pair<string,int>(beginStr,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;
            //26个字母替换
            for(int j=0;j<26;j++){
                newWord[i]=j+'a';
                if(newWord==endStr){//找到终点
                    std::cout << path +1<< std::endl;
                    return 0;
                }
                // 字符串集合里出现了newWord,并且newWord没有被访问过
                if (strSet.find(newWord) != strSet.end()
                        && visitMap.find(newWord) == visitMap.end()) {
                    // 添加访问信息,并将新字符串放到队列中
                    visitMap.insert(pair<string, int>(newWord, path + 1));
                    que.push(newWord);
                }
                
            }
        }
    }
    cout<<0<<endl;
}

题目:有向图的完全可达性

105. 有向图的完全可达性 (kamacoder.com)

题目描述

给定一个有向图,包含 N 个节点,节点编号分别为 1,2,...,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

输入描述

第一行包含两个正整数,表示节点数量 N 和边的数量 K。 后续 K 行,每行两个正整数 s 和 t,表示从 s 节点有一条边单向连接到 t 节点。

输出描述

如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

题目分析:

因为这里使用的时acm模式,需要确认的时,有向图的存储我们使用邻接表。

思路其实不复杂,我们已经知道了广搜和深搜,那么搜索过的结点标记上,最后如果没有未标记的结点即可完全可达。还是使用广搜的解法。

cpp 复制代码
#include<iostream>
#include<vector>
#include <list>

using namespace std;

//key 当前遍历的结点
void dfs(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<visited.size();i++){
        if(visited[i]==false){
            cout<<-1<<endl;
            return 0;
        }
    }
    cout<<1<<endl;
}

题目:岛屿的周长

106. 岛屿的周长 (kamacoder.com)

题目描述

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

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

输入描述

第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。

输出描述

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

题目分析:

又是岛屿问题。深搜还是广搜?但是这一题有必要吗?没有必要,求周长而已,遍历所有点,陆地区域就找四个方向为海洋区域,周长就增加。

cpp 复制代码
#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];    // 计算周边坐标x,y
                    if (x < 0                       // x在边界上
                            || x >= grid.size()     // x在边界上
                            || y < 0                // y在边界上
                            || y >= grid[0].size()  // y在边界上
                            || grid[x][y] == 0) {   // x,y位置是水域
                        result++;
                    }
                }
            }
        }
    }
    cout << result << endl;

}

对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。

代码随想录 (programmercarl.com)

已刷题目:139
相关推荐
忘梓.36 分钟前
解锁动态规划的奥秘:从零到精通的创新思维解析(2)
算法·动态规划
TANGLONG2221 小时前
【初阶数据结构与算法】八大排序算法之交换排序(冒泡排序,快速排序---hoare、挖坑法、lomuto双指针3种版本)
c语言·数据结构·c++·算法·面试·蓝桥杯·排序算法
带多刺的玫瑰1 小时前
Leecode刷题C语言之同位字符串连接的最小长度
c语言·数据结构·算法
code monkey.1 小时前
【排序算法】—— 归并排序
c++·算法·排序算法
就爱学编程2 小时前
重生之我在异世界学编程之算法与数据结构:深入动态顺序表篇
数据结构·windows·算法
北国无红豆5 小时前
【LeetCode】35.搜索插入位置
算法·leetcode·职场和发展
宸码8 小时前
【机器学习】【集成学习——决策树、随机森林】从零起步:掌握决策树、随机森林与GBDT的机器学习之旅
人工智能·python·算法·决策树·随机森林·机器学习·集成学习
YSRM9 小时前
leetcode--字符串
算法·leetcode·职场和发展
phenomenal999 小时前
二叉树总结
算法
小陈phd10 小时前
深度学习之目标检测——RCNN
python·深度学习·算法·计算机视觉