当前位置: 首页 > news >正文

代码随想录第51 52天 | 图论-岛屿问题汇总

图论基础介绍传送门:代码随想录第50天 | 图论 基础介绍(新篇章-CSDN博客

岛屿数量 深搜

岛屿数量 深搜

深度搜索是dfs,思路是:遍历矩阵的所有,找到地面,此时岛屿数+1,接着进行dfs,把与该地面相连的所有地面(上下左右四个方向),全部设为0,也就是地面记为已访问。再接着去找新的地面

1. 下面这是一种写法,当然也可以用visited数组来去记录地面已经访问.(重点在于这个dfs无结束条件——这是因为在for循环去遍历上下左右过程中就已经把不符合条件的给筛掉了。)

#include<iostream>
#include <vector>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void dfs(vector<vector<int>>& c,int x,int y){//找与其连接的上下左右四个方向的!for(int i=0;i<4;i++){int nx=x+dx[i];int ny=y+dy[i];if (nx<0||nx>=c.size()|| ny<0 || ny>=c[0].size()||c[nx][ny]==0) {continue;}c[nx][ny]=0;//把遍历的位置进行初始化dfs(c,nx,ny);}
}int main(){//dfs的思路是,遍历整个矩阵,找到是陆地的,则岛屿数+1,且把这个矩阵周围的挨着的都记录为已被访问(可以赋值为0),继续遍历找陆地int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}int res=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res++;c[i][j]=0;//因为dfs不是先进行操作当前位置,而是操作下一个位置,则需要提前设为0dfs(c,i,j);}}}cout<<res;return 0;
}

版本二:也可以把终止条件写在外面:

#include<iostream>
#include <vector>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void dfs(vector<vector<int>>& c,int x,int y){if(c[nx][ny]==0) return;//当这个地面被访问过或者是本身就不是地面,则不符合条件c[x][y]=0;//dfs先处理当前位置
、for(int i=0;i<4;i++){int nx=x+dx[i];int ny=y+dy[i];if (nx<0||nx>=c.size()|| ny<0 || ny>=c[0].size()) {continue;}dfs(c,nx,ny);}
}int main(){//dfs的思路是,遍历整个矩阵,找到是陆地的,则岛屿数+1,且把这个矩阵周围的挨着的都记录为已被访问(可以赋值为0),继续遍历找陆地int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}int res=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res++;//既然先处理当前位置,则这里不用再让c[i][j]设为0dfs(c,i,j);}}}cout<<res;return 0;
}

方法1是 :dfs是处理下一个节点,判断是否能合法,则再传进dfs函数的就是合法节点。

方法2是:dfs先处理当前节点,且处理节点前要判断是否符合要求:被访问或者是否为地面?

岛屿数量 广搜

岛屿数量 广搜

其实这个广搜的思路和深搜的思路一样:都是先遍历矩阵,找到地面,然后岛屿数量++,接着寻找与这块地面相连的所有地面!设为已访问(可以采用visited数组,或者直接设矩阵对应位置为0)

区别是:BFS 使用队列,将当前陆地的邻居加入队列,并标记为已访问

这个过程的重点在于:只要加入队列,立刻标记,而不是:从队列中取出节点再标记

#include<iostream>
#include <vector>
#include<queue>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void bfs(vector<vector<int>>& c,int x,int y){queue<pair<int, int>> q;q.push({x,y});//放入队列则立即进行已访问的标记c[x][y]=0;while(!q.empty()){pair<int ,int> cur = q.front(); //队列出来方向的第一个元素q.pop();//记录了则弹出队列元素int curx = cur.first;int cury = cur.second;for(int i=0;i<4;i++){int nx=curx+dx[i];int ny=cury+dy[i];if (nx<0||nx>=c.size()|| ny<0 || ny>=c[0].size()||c[nx][ny]==0) {continue;}c[nx][ny] = 0;      // 标记为已访问q.push({nx, ny});}}}int main(){int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}int res=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res++;bfs(c,i,j);}}}cout<<res;return 0;
}

岛屿的最大面积

岛屿的最大面积

DFS:

#include<iostream>
#include<vector>
using namespace std;int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};int res;
void dfs(vector<vector<int>>& c,int x,int y){if(c[x][y]==0) return;c[x][y]=0;res++;for(int i=0;i<4;i++){int nx=dx[i]+x;int ny=dy[i]+y;if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()){continue;}dfs(c,nx,ny);}
}int main(){//使用dfsint n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}int result=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res=0;//每次要初始化一下,dfs先处理当前节点dfs(c,i,j);result = max(result, res);}}}cout<<result;return 0;
}

BFS:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};int res;
void bfs(vector<vector<int>>& c,int x,int y){if(c[x][y]==0) return;queue<pair<int,int>> q;q.push({x,y});c[x][y]=0;res++;while(!q.empty()){pair<int,int> it=q.front();q.pop();int xx=it.first;int yy=it.second;for(int i=0;i<4;i++){int nx=dx[i]+xx;int ny=dy[i]+yy;if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()){continue;}c[nx][ny] = 0;      // 标记为已访问q.push({nx, ny});}}
}int main(){//使用bfsint n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}int result=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res=0;bfs(c,i,j);result = max(result, res);}}}cout<<result;return 0;
}

孤岛的总面积

孤岛的总面积

首先,明确什么是孤岛?——岛屿中无任何与边缘接触的地面!

1. 先通过dfs把与边缘接触的岛屿都记录为已访问

2. 再遍历记录剩余岛屿的面积

#include<iostream>
#include<vector>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};void dfs(vector<vector<int>>& c,int x,int y){//先处理当前坐标if(x<0||x>=c.size()||y<0||y>=c[0].size()||c[x][y]==0){return;}c[x][y]=0;for(int i=0;i<4;i++){int nx=x+dx[i];int ny=y+dy[i];dfs(c,nx,ny);}
}int main(){//孤岛是位于矩阵内部,所有单元格都不接触边缘的岛屿int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}// 移除接触边缘的岛屿for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if ((i == 0 || i == n-1 || j == 0 || j == m-1) && c[i][j] == 1) {//若是接触边缘的且是地面的则一定不符合要求!直接标记周围的为已访问dfs(c, i, j);}}}int res=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res++;//现在去掉了所有的不符合要求的岛屿,则直接统计}}}cout<<res;return 0;
}

BFS:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};void bfs(vector<vector<int>>& c,int x,int y){queue<pair<int,int>> q;q.push({x,y});c[x][y]=0;while(!q.empty()){pair<int,int> it=q.front();q.pop();int cx=it.first;int cy=it.second;for(int i=0;i<4;i++){int nx=cx+dx[i];int ny=cy+dy[i];if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()||c[nx][ny]==0){continue;}c[nx][ny] = 0;  // 标记为已访问q.push({nx, ny});  // 入队,不是递归调用}}
}int main(){//孤岛是位于矩阵内部,所有单元格都不接触边缘的岛屿int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}// 移除接触边缘的岛屿for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if ((i == 0 || i == n-1 || j == 0 || j == m-1) && c[i][j] == 1) {//若是接触边缘的且是地面的则一定不符合要求!直接标记周围的为已访问bfs(c, i, j);}}}int res=0;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res++;//现在去掉了所有的不符合要求的岛屿,则直接统计}}}cout<<res;return 0;
}

沉没孤岛

沉没孤岛

DFS:

#include<iostream>
#include<vector>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void dfs(vector<vector<int>>& c,int x,int y){c[x][y]=0;for(int i=0;i<4;i++){int nx=x+dx[i];int ny=y+dy[i];if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()||c[nx][ny]==0){continue;}dfs(c,nx,ny);}
}int main(){//孤岛是位于矩阵内部,所有单元格都不接触边缘的岛屿int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}vector<vector<int>> res(c);// 移除接触边缘的岛屿,也就是非孤岛for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if ((i == 0 || i == n-1 || j == 0 || j == m-1) && c[i][j] == 1) {dfs(c, i, j);}}}for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res[i][j]=0;//现在去掉了所有的不符合要求的岛屿,则直接统计}}}for(int i=0;i<n;i++){//最终输出for(int j=0;j<m;j++){cout<<res[i][j]<<" ";}cout<<endl;}return 0;
}

BFS:(思路一样,只不过一个是深度搜索的逻辑,一个是广度搜索

#include<iostream>
#include<vector>
#include<queue>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
void bfs(vector<vector<int>>& c,int x,int y){queue<pair<int,int>> q;q.push({x,y});c[x][y]=0;while(!q.empty()){pair<int,int> it=q.front();q.pop();int cx=it.first;int cy=it.second;for(int i=0;i<4;i++){int nx=cx+dx[i];int ny=cy+dy[i];if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()||c[nx][ny]==0){continue;}q.push({nx,ny});c[nx][ny]=0;}}
}int main(){//孤岛是位于矩阵内部,所有单元格都不接触边缘的岛屿int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}vector<vector<int>> res(c);// 移除接触边缘的岛屿,也就是非孤岛for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if ((i == 0 || i == n-1 || j == 0 || j == m-1) && c[i][j] == 1) {bfs(c, i, j);}}}for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(c[i][j]==1){res[i][j]=0;//现在去掉了所有的不符合要求的岛屿,则直接统计}}}for(int i=0;i<n;i++){//最终输出for(int j=0;j<m;j++){cout<<res[i][j]<<" ";}cout<<endl;}return 0;
}

水流问题

水流问题

假设要遍历每个点,每个点都需要进行广度或深度搜索(这个是需要遍历所有节点的),则时间复杂度为:O(n*m*n*m),前一个n*m表示遍历每个点去深搜或者广搜的时间复杂度,后面的是指广搜和深搜的时间复杂度

显而易见会超时

假设使用逆向思维:(上面两题去先去找边缘的陆地,从而去找出不符合要求的岛屿)这一题,也可以从边缘出发,假设从第一边界和第二边界出发,去往高处走……

#include<iostream>
#include<vector>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};void dfs(vector<vector<int>>& c,int x,int y,vector<vector<bool>>& visited){//使用引用,这样可以直接赋值visited[x][y]=true;for(int i=0;i<4;i++){int nx=x+dx[i];int ny=y+dy[i];if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()){continue;}if(c[x][y]<=c[nx][ny]&&!visited[nx][ny]){//且未被访问!dfs(c,nx,ny,visited);}}
}int main(){int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));//数值表示该位置的相对高度for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}//水流流向等高或较低的相邻(上下左右)的地点//第一组边界是左和上vector<vector<bool>> firstb(n,vector<bool>(m,false));for(int i=0;i<n;i++){if(!firstb[i][0]) dfs(c,i,0,firstb);}for(int j=0;j<m;j++){if(!firstb[0][j]) dfs(c,0,j,firstb);}//第二组边界是右和下vector<vector<bool>> secondb(n,vector<bool>(m,false));for(int i=0;i<n;i++){if(!secondb[i][m-1]) dfs(c,i,m-1,secondb);}for(int j=0;j<m;j++){if(!secondb[n-1][j]) dfs(c,n-1,j,secondb);}for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(firstb[i][j]&&secondb[i][j]){cout<<i<<" "<<j<<endl;}}}return 0;
}

BFS:

#include<iostream>
#include<vector>
#include<queue>
using namespace std;int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};void bfs(vector<vector<int>>& c,int x,int y,vector<vector<bool>>& visited){//使用引用,这样可以直接赋值queue<pair<int,int>> q;q.push({x,y});visited[x][y]=true;while(!q.empty()){auto it=q.front();q.pop();int cx=it.first;int cy=it.second;for(int i=0;i<4;i++){int nx=cx+dx[i];int ny=cy+dy[i];if(nx<0||nx>=c.size()||ny<0||ny>=c[0].size()){continue;}if(c[cx][cy]<=c[nx][ny]&&!visited[nx][ny]){//且未被访问!q.push({nx,ny});visited[nx][ny]=true;}}}}int main(){int n,m;cin>>n>>m;vector<vector<int>> c(n,vector<int>(m));//数值表示该位置的相对高度for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>c[i][j];}}//水流流向等高或较低的相邻(上下左右)的地点//第一组边界是左和上vector<vector<bool>> firstb(n,vector<bool>(m,false));for(int i=0;i<n;i++){if(!firstb[i][0]) bfs(c,i,0,firstb);}for(int j=0;j<m;j++){if(!firstb[0][j]) bfs(c,0,j,firstb);}//第二组边界是右和下vector<vector<bool>> secondb(n,vector<bool>(m,false));for(int i=0;i<n;i++){if(!secondb[i][m-1]) bfs(c,i,m-1,secondb);}for(int j=0;j<m;j++){if(!secondb[n-1][j]) bfs(c,n-1,j,secondb);}for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(firstb[i][j]&&secondb[i][j]){cout<<i<<" "<<j<<endl;}}}return 0;
}

建造最大岛屿

104.建造最大岛屿

首先想到的是一个暴力的解法:遍历矩阵,若遇到海洋,则把其变为陆地,计算最大岛屿面积(深搜或者广搜),最终找这些最大岛屿面积中的最大值。

优化思路是:

先进行一次搜索把每块岛屿,进行编号+记录其面积

再遍历海洋,遇到海洋若其四周有岛屿,则直接1+该岛屿的面积,直接得出最大岛屿面积,而不需要再去深搜或广搜去计算这个最大岛屿面积(也就是主要优化掉上面蓝字部分

  1. 第一次遍历:给每个岛屿编号(从0开始增加),且记录每个岛屿的面积(也需要有一个count遍历先记录)(使用islandArea[id]来记录编号id的岛屿面积

  2. 第二次遍历⭐:遇到海洋时,设置初始面积为1,查看四周的岛屿(遍历上下左右四个方向)(需要把岛屿位置和岛屿对应上,用islandID[x][y]来记录(x,y)属于哪个岛屿),若周围是陆地,则获取位置对应的编号,则加上这个岛屿的面积(前提是没有加上)

    1. 这里还需要一个去重数组,记录是否加上过该岛屿面积。

    2. 使用unordered_set去自动去重

#include<iostream>
#include<vector>
#include<unordered_set>
using namespace std;int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};vector<vector<bool>> visited;
vector<vector<int>> islandID;
vector<int> islandArea;
int currentIsland = 0;
int currentCount = 0;void dfs(vector<vector<int>>& c, int x, int y) {//第一次遍历visited[x][y] = true;islandID[x][y] = currentIsland;currentCount++;for(int i = 0; i < 4; i++) {int nx = x + dx[i];int ny = y + dy[i];if(nx < 0 || nx >= c.size() || ny < 0 || ny >= c[0].size() || visited[nx][ny] || c[nx][ny] == 0) {continue;}dfs(c, nx, ny);}
}// 计算填海后的面积,第二次遍历
int calculateArea(vector<vector<int>>& c, int i, int j) {unordered_set<int> neighborIslands;  // 用set自动去重int total = 1;  // 填海后当前位置for(int k = 0; k < 4; k++) {int ni = i + dx[k];int nj = j + dy[k];if(ni >= 0 && ni < c.size() && nj >= 0 && nj < c[0].size() && c[ni][nj] == 1) {int id = islandID[ni][nj];neighborIslands.insert(id);  // 插入进这个岛屿的id,且会自动去重}}// 加上所有不同岛屿的面积(没加过的岛屿面积for(int id : neighborIslands) {total += islandArea[id];}return total;
}int main() {int n, m;cin >> n >> m;vector<vector<int>> c(n, vector<int>(m));for(int i = 0; i < n; i++) {for(int j = 0; j < m; j++) {cin >> c[i][j];}}visited = vector<vector<bool>>(n, vector<bool>(m, false));islandID = vector<vector<int>>(n, vector<int>(m, -1));// 第一步:给岛屿编号for(int i = 0; i < n; i++) {for(int j = 0; j < m; j++) {if(c[i][j] == 1 && !visited[i][j]) {currentCount = 0;dfs(c, i, j);islandArea.push_back(currentCount);currentIsland++;}}}// 第二步:找最大填海面积int maxArea = 0;for(int i = 0; i < n; i++) {for(int j = 0; j < m; j++) {if(c[i][j] == 0) {int area = calculateArea(c, i, j);maxArea = max(maxArea, area);//记录最大的面积}}}// 如果全是陆地if(maxArea == 0 && !islandArea.empty()) {for(int area : islandArea) {maxArea = max(maxArea, area);}}cout << maxArea << endl;return 0;
}

岛屿的周长

岛屿的周长

这题首先分析,单独一个地面的周长是多少?——4,若周围出现地面,则周长减去1

则我们只需要遍历每个地面,求出其周围(上下左右四个方向)是否有地面?有则减去1

则实际上不需要dfs或者bfs进行操作

#include<iostream>
#include<vector>
using namespace std;int main() {int n, m;cin >> n >> m;vector<vector<int>> grid(n, vector<int>(m));for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {cin >> grid[i][j];}}int perimeter = 0;int dx[] = {0, 0, -1, 1};int dy[] = {1, -1, 0, 0};for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (grid[i][j] == 1) {// 每个陆地格子初始周长贡献为4int count = 4;// 检查四个方向for (int k = 0; k < 4; k++) {int ni = i + dx[k];int nj = j + dy[k];// 如果相邻是陆地,减去1if (ni >= 0 && ni < n && nj >= 0 && nj < m && grid[ni][nj] == 1) {count--;}}perimeter += count;}}}cout << perimeter << endl;return 0;
}
http://www.dtcms.com/a/548610.html

相关文章:

  • 分布式存储:Ceph、GlusterFS、MinIO架构与部署
  • 机械外协加工网最新订单移动网站如何优化排名
  • 11 种方法解决小米/米手机无法通过 USB 连接电脑的问题
  • Ubuntu:设置程序开机自启动
  • 化妆品品牌网站如何做wordpress pdf文章
  • vue 网站导航栏
  • 如何提高 IPA 安全性 多工具组合打造可复用的 iOS 加固与反编译防护体系(IPA 安全 iOS 加固 无源码混淆 Ipa Guard 实战)
  • 上海市工程建设交易中心网站深圳公司广告片制作
  • FreeRTOS 学习:(三)HAL库、标准库 和 FreeRTOS 的关联性,简述
  • 使用 Tauri + Rust 构建跨平台桌面应用:前端技术的新边界
  • 如何录屏?【图文详解】免费录屏软件?电脑如何录屏?电脑怎么录屏?
  • 深入Rust:Box、Rc、Arc智能指针机制解析与实践指南
  • 【项目实践】公寓租赁项目(十):基于SpringBoot登录管理接口开发
  • Java1030 abstract 继承
  • 第六部分:VTK进阶(第180章 重采样与插值)
  • 聊城做网站推广哪家好android sdk
  • 时间序列早期分类中的置信度累积问题:从ECE-C到时序依赖建模
  • Rust + WebAssembly + Svelte + TypeScript + Zod 全栈开发深度指南
  • 【android bluetooth 协议分析 18】【PBAP详解 2】【车机为何不显示电话号码为空的联系人信息】
  • MacPro2012升级Monterey终极解决方案
  • 软件项目管理工具
  • Actix Web适合什么类型的Web应用?可以部署 Java 或 .NET 的应用程序?
  • Android PDF 操作 - AndroidPdfViewer 弹出框显示 PDF
  • 做新零售这些注册网站和找货源6内蒙古网站建设流程
  • 【Linux篇】进程间通信 - 匿名管道
  • Java Stream Collectors主要 API 详解
  • Mac如何安装mysql和完全卸载mysql
  • 【Docker】【03.使用docker搭建ubuntu20.04 Qt5.12 开发环境】
  • uni-app 上架 iOS 应用全流程 从云打包到开心上架(Appuploader)免 Mac 上传发布指南
  • 深圳公司网站设计公司wordpress春菜