代码随想录算法训练营day52:图03:101. 孤岛的总面积;102. 沉没孤岛;103. 水流问题
101. 孤岛的总面积
卡码网:101. 孤岛的总面积(opens new window)
题目描述
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。
现在你需要计算所有孤岛的总面积,岛屿面积的计算方式为组成岛屿的陆地的总数。
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0。
输出描述
输出一个整数,表示所有孤岛的总面积,如果不存在孤岛,则输出 0。
我的算法dfs:
在dfs的基础上,分别计算每次孤岛的面积,计入thissize,计算完后加到total上
每次遇到一个陆地且未访问过:进行dfs访问
核心:一旦遇到一个陆地接触到了边界——那么thissize将始终归为0
即使thissize归为0,也需要计算把相接处的陆地visit掉
所以,采用了在dfs函数开头分类讨论:
遇到边界了——size为0
本情况非孤岛,size已经为0了——永远为0
初始情况,size记为-1,且初始情况 不涉及到边界——size=1
否则初始情况标记为0,会和非孤岛 size已经为0混淆
其他情况下size++
注意:初始情况,是怎么被启动的!!
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>int move[4][2]={1,0,0,1,-1,0,0,-1};
int this_size;void dfs(int i,int j,int **box,int**visit,int m,int n){if(i<=0 || i>=n-1 || j<=0 || j>=m-1 || this_size==0){this_size=0;}else if(this_size==-1) this_size=1;else this_size++;for(int k=0;k<4;k++){int move_i = i+move[k][0];int move_j = j+move[k][1];if(move_i >=0 && move_i<n && move_j>=0 && move_j<m && box[move_i][move_j]==1 && visit[move_i][move_j]==0){visit[move_i][move_j]=1;//printf("in:%d %d size=%d\n",move_i,move_j,this_size);dfs(move_i,move_j,box,visit,m,n);}}
}int main(){int n,m;scanf("%d%d",&n,&m);int** box=(int**)malloc(sizeof(int*)*n);int **visit=(int**)malloc(sizeof(int*)*n);for (int i=0;i<n;i++){box[i]=(int*)malloc(sizeof(int)*m);visit[i]=(int*)malloc(sizeof(int)*m);for(int j=0;j<m;j++){scanf("%d",&box[i][j]);visit[i][j]=0;}}int total_area=0;for (int i=0;i<n;i++){for(int j=0;j<m;j++){if(box[i][j]==1 && visit[i][j]==0){this_size=-1;visit[i][j]=1;dfs(i,j,box,visit,m,n);total_area=total_area+this_size;}}}printf("%d",total_area);return 0;
}
改成bfs:
int front=-1;
int rear=-1;
int queue[10000][2];bool judge(int i,int j,int n,int m){if(i<= 0 || i>=n-1 || j<=0 || j>=m-1) return true;else return false;
}void bfs(int i,int j,int**box,int**visit,int m,int n){rear++;queue[rear][0]=i;queue[rear][1]=j;visit[i][j]=1;if(judge(i,j,n,m)) this_size=0;else this_size=1;while(front!=rear){front++;int xi=queue[front][0];int xj=queue[front][1];for(int k=0;k<4;k++){int fi=xi+move[k][0];int fj=xj+move[k][1];if(fi>=0 && fi<n && fj>=0 && fj<m && box[fi][fj]==1 && visit[fi][fj]==0){if(judge(fi,fj,n,m) || this_size==0) this_size=0;else this_size++;rear++;queue[rear][0]=fi;queue[rear][1]=fj;visit[fi][fj]=1;}}}
}
或者
也可以考虑:
本题要求找到不靠边的陆地面积,那么我们只要从周边找到陆地然后 通过 dfs或者bfs 将周边靠陆地且相邻的陆地都变成海洋,然后再去重新遍历地图 统计此时还剩下的陆地就可以了。
如图,在遍历地图周围四个边,靠地图四边的陆地,都为绿色,

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

102. 沉没孤岛
卡码网题目链接(ACM模式)(opens new window)
题目描述:
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单元格包围。孤岛是那些位于矩阵内部、所有单元格都不接触边缘的岛屿。
现在你需要将所有孤岛“沉没”,即将孤岛中的所有陆地单元格(1)转变为水域单元格(0)。
输入描述:
第一行包含两个整数 N, M,表示矩阵的行数和列数。
之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。
输出描述
输出将孤岛“沉没”之后的岛屿矩阵。
分析:
思路依然是从地图周边出发,将周边空格相邻的陆地都做上标记,然后在遍历一遍地图,遇到 陆地 且没做过标记的,那么都是地图中间的 陆地 ,全部改成水域就行。
有的录友可能想,我在定义一个 visited 二维数组,单独标记周边的陆地,然后遍历地图的时候同时对 数组board 和 数组visited 进行判断,决定 陆地是否变成水域。
这样做其实就有点麻烦了,不用额外定义空间了,标记周边的陆地,可以直接改陆地为其他特殊值作为标记。
步骤一:深搜或者广搜将地图周边的 1 (陆地)全部改成 2 (特殊标记)
步骤二:将水域中间 1 (陆地)全部改成 水域(0)
步骤三:将之前标记的 2 改为 1 (陆地)
如图:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>int move[4][2]={1,0,0,1,-1,0,0,-1};void dfs(int i,int j,int**box,int **visit,int m,int n){visit[i][j]=1;box[i][j]=2;for(int k=0;k<4;k++){int movei=i+move[k][0];int movej=j+move[k][1];if(movei>=0 && movej>=0 && movei<n && movej<m && visit[movei][movej]==0 && box[movei][movej]==1){dfs(movei,movej,box,visit,m,n);}}
}int main(){int n,m;scanf("%d%d",&n,&m);int** box=(int**)malloc(sizeof(int*)*n);int **visit=(int**)malloc(sizeof(int*)*n);for (int i=0;i<n;i++){box[i]=(int*)malloc(sizeof(int)*m);visit[i]=(int*)malloc(sizeof(int)*m);for(int j=0;j<m;j++){scanf("%d",&box[i][j]);visit[i][j]=0;}}for (int i=0;i<n;i++){if(box[i][0]==1 && visit[i][0]==0){dfs(i,0,box,visit,m,n);}if(box[i][m-1]==1 && visit[i][m-1]==0){dfs(i,m-1,box,visit,m,n);}}for (int i=0;i<n;i++){if(box[0][i]==1 && visit[0][i]==0){dfs(0,i,box,visit,m,n);}if(box[n-1][i]==1 && visit[n-1][i]==0){dfs(n-1,i,box,visit,m,n);}}for (int i=0;i<n;i++){for(int j=0;j<m;j++){if(box[i][j]==1) box[i][j]=0;else if(box[i][j]==2) box[i][j]=1;printf("%d ",box[i][j]);}printf("\n");}return 0;
}
或者bfs:
int front=-1,rear=-1;
int queue[1000][2];void bfs(int i,int j,int**box,int **visit,int m,int n){rear++;queue[rear][0]=i;queue[rear][1]=j;visit[i][j]=1;box[i][j]=2;while(rear!=front){front++;int xi=queue[front][0];int xj=queue[front][1];for(int k=0;k<4;k++){int movei=xi+move[k][0];int movej=xj+move[k][1];if(movei>=0 && movej>=0 && movei<n && movej<m && visit[movei][movej]==0 && box[movei][movej]==1){rear++;queue[rear][0]=movei;queue[rear][1]=movej;visit[movei][movej]=1;box[movei][movej]=2;}}}}
103. 水流问题
卡码网题目链接(ACM模式)(opens new window)
题目描述:
现有一个 N × M 的矩阵,每个单元格包含一个数值,这个数值代表该位置的相对高度。矩阵的左边界和上边界被认为是第一组边界,而矩阵的右边界和下边界被视为第二组边界。
矩阵模拟了一个地形,当雨水落在上面时,水会根据地形的倾斜向低处流动,但只能从较高或等高的地点流向较低或等高并且相邻(上下左右方向)的地点。我们的目标是确定那些单元格,从这些单元格出发的水可以达到第一组边界和第二组边界。
输入描述:
第一行包含两个整数 N 和 M,分别表示矩阵的行数和列数。
后续 N 行,每行包含 M 个整数,表示矩阵中的每个单元格的高度。
输出描述:
输出共有多行,每行输出两个整数,用一个空格隔开,表示可达第一组边界和第二组边界的单元格的坐标,输出顺序任意。
分析:
一、从高往低寻找
小的数 无法继承大的数的遍历结果,导致每个数字都必须从头遍历,复杂度太高
遍历每一个节点,是 m * n,遍历每一个节点的时候,都要做深搜,深搜的时间复杂度是: m * n
那么整体时间复杂度 就是 O(m^2 * n^2) ,这是一个四次方的时间复杂度。

二、从低往高
从第一组边界上的节点 逆流而上,将遍历过的节点都标记上。
同样从第二组边界的边上节点 逆流而上,将遍历过的节点也标记上。
然后两方都标记过的节点就是既可以流太平洋也可以流大西洋的节点。
从第一组边界边上节点出发,如图:

从第二组边界上节点出发,如图:

关于dfs函数搜索的过程 时间复杂度是 O(n * m),这个大家比较容易想。
关键看主函数,那么每次dfs的时候,上面还是有for循环的。
第一个for循环,时间复杂度是:n * (n * m) 。
第二个for循环,时间复杂度是:m * (n * m)。
所以本题看起来 时间复杂度好像是 : n * (n * m) + m * (n * m) = (m * n) * (m + n) 。
其实这是一个误区,大家再自己看 dfs函数的实现,其实 有visited函数记录 走过的节点,而走过的节点是不会再走第二次的。
所以 调用dfs函数,只要参数传入的是 数组 firstBorder,那么地图中 每一个节点其实就遍历一次,无论你调用多少次。
同理,调用dfs函数,只要 参数传入的是 数组 secondBorder,地图中每个节点也只会遍历一次。
所以,以下这段代码的时间复杂度是 2 * n * m。 地图用每个节点就遍历了两次,参数传入 firstBorder 的时候遍历一次,参数传入 secondBorder 的时候遍历一次。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>int move[4][2]={1,0,0,1,-1,0,0,-1};int main(){int n,m;scanf("%d%d",&n,&m);int** box=(int**)malloc(sizeof(int*)*n);int **visit1=(int**)malloc(sizeof(int*)*n);int **visit2=(int**)malloc(sizeof(int*)*n);for (int i=0;i<n;i++){box[i]=(int*)malloc(sizeof(int)*m);visit1[i]=(int*)malloc(sizeof(int)*m);visit2[i]=(int*)malloc(sizeof(int)*m);for(int j=0;j<m;j++){scanf("%d",&box[i][j]);visit1[i][j]=0;visit2[i][j]=0;}}for (int i=0;i<n;i++){if(visit1[i][0]==0) dfs(i,0,box,visit1,m,n);if(visit2[i][m-1]==0) dfs(i,m-1,box,visit2,m,n); }for (int i=0;i<m;i++){if(visit1[0][i]==0) dfs(0,i,box,visit1,m,n);if( visit2[n-1][i]==0) dfs(n-1,i,box,visit2,m,n);}for (int i=0;i<n;i++){for(int j=0;j<m;j++){if(visit1[i][j]==1 && visit2[i][j]==1) printf("%d %d\n",i,j); }}return 0;
}
bfs:
int front=-1,rear=-1;
int queue[1000][2];void bfs(int i,int j,int**box,int **visit,int m,int n){rear++;queue[rear][0]=i;queue[rear][1]=j;visit[i][j]=1;while(rear!=front){front++;int xi=queue[front][0];int xj=queue[front][1];for(int k=0;k<4;k++){int movei=xi+move[k][0];int movej=xj+move[k][1];if(movei>=0 && movej>=0 && movei<n && movej<m && visit[movei][movej]==0 && box[movei][movej]>=box[xi][xj]){rear++;queue[rear][0]=movei;queue[rear][1]=movej;visit[movei][movej]=1;}}}}
相关文章:
代码随想录算法训练营day52:图03:101. 孤岛的总面积;102. 沉没孤岛;103. 水流问题
101. 孤岛的总面积 卡码网:101. 孤岛的总面积(opens new window) 题目描述 给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿指的是由水平或垂直方向上相邻的陆地单元格组成的区域,且完全被水域单…...
开源大模型本地私有化部署
1、安装ollama ollma下载 https://ollama.com/download/windows linux 安装 curl -fsSL https://ollama.com/install.sh | sh 运行 ollama run gemma:2b ollama run gemma:7b 使用端口11434 2、下载 open-webui 代码 https://github.com/open-webui/open-webui.git 生成目录…...
站长为什么要搭建个人博客网站
搭建个人博客网站是一个值得考虑的选择,它不仅有助于个人成长,还能在多个方面带来积极的影响。以下是几个主要的理由: 一、记录与备忘 方便回顾与查阅:博客网站成为了一个个人知识库,记录下来的内容方便后续查阅和回顾…...
Golang | Leetcode Golang题解之第355题设计推特
题目: 题解: type Twitter struct {Tweets []intUserTweets map[int][]intFollows map[int][]intIsFollowMy map[int]bool }/** Initialize your data structure here. */ func Constructor() Twitter {// 每一次实例化的时候,都重新分配一次…...
Redis如何实现发布/订阅?
引言 Redis是一款高性能的内存数据存储系统,除了常用的键值存储功能外,还提供了发布/订阅(Pub/Sub)机制。通过发布/订阅机制,Redis可以实现消息的广播或者实时通知功能,是一种非常有用的功能。 本文将详细…...
EmguCV学习笔记 VB.Net 4.4 图像形态学
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客 教程C#版本请访问:EmguCV学习笔记 C# 目录-CSD…...
HarmonyOS 开发
环境 下载IDE 代码 import { hilog } from kit.PerformanceAnalysisKit; import testNapi from libentry.so; import { router } from kit.ArkUI; import { common, Want } from kit.AbilityKit;Entry Component struct Index {State message: string Hello HarmonyOS!;p…...
拒绝拖延!Kimi助你一天内速成论文初稿!
撰写学术论文是一项需要周密计划和精确执行的任务。它要求作者对文章的每个部分进行深入思考,以确保论文结构的合理性和论述的清晰度。利用Kimi的功能,我们可以更系统地进行写作,从构思到最终成稿,逐步构建出一篇高质量的学术论文…...
Python画笔案例-005 绘制迷宫
1、绘制迷宫 通过 python 的turtle 库绘制一个迷宫的图案,如下图: 2、实现代码 从图上可以看出,内测最短的竖线开始,每次右转 90 度后,线段都增加 8 个单位,所以我们是用 for 循环,循环 50 次…...
【鸿蒙学习】HarmonyOS应用开发者高级认证 - 应用性能优化二(代码层面)
学完时间:2024年8月22日 学完排名:第1801名 一、长列表优化概述 列表是应用开发中最常见的一类开发场景,它可以将杂乱的信息整理成有规律、易于理解和操作的形式,便于用户查找和获取所需要的信息。应用程序中常见的列表场景有新…...
【Docker】如何将A机器内的镜像,导入到B机器?
由于网络或者仓库的原因,经常遇到pull拉取镜像失败的情况!! 那么,如何将A机器内的镜像,通过命令,导入到B机器? 两条重要的命令: 1,在已经成功拉取pull的机器上执行命令…...
动手实现基于Reactor模型的高并发Web服务器(一):epoll+多线程版本
系统流程概览 main函数 对于一个服务器程序来说,因为要为外部的客户端程序提供网络服务,也就是进行数据的读写,这就必然需要一个 socket 文件描述符,只有拥有了文件描述符 C/S 两端才能通过 socket 套接字进行网络通信࿰…...
爬虫案例4——爬取房天下数据
简介:个人学习分享,如有错误,欢迎批评指正 任务:从房天下网中爬取小区名称、地址、价格和联系电话 目标网页地址:https://newhouse.fang.com/house/s/ 一、思路和过程 目标网页具体内容如下: …...
网络硬盘录像机NVR程序源码NVR全套运用方案
在当今社会,随着科技的飞速发展和人们对安全需求的日益增长,安防监控系统已成为保障公共安全、维护社会稳定的重要手段。其中,网络视频录像机(NVR)作为安防监控系统的核心设备,其智能化升级运用方案对于提高…...
03:电容的充放电特性及应用举例
1.电容的基本特性:电容两端的电压不能突变 2.影响电容两端电压的参数:整个回路中电阻,电容大小 3.如何计算电容的电压变化时间? τRC R1k C1uF 则得到τ1ms的时间 应用:芯片使能延时...
【专题】2023-2024中国游戏企业研发竞争力报告合集PDF分享(附原数据表)
原文链接: https://tecdat.cn/?p37447 在当今的数字时代,游戏产业已然成为经济与文化领域中一股不可忽视的重要力量。2023 年,中国自研游戏市场更是呈现出一片繁荣且复杂的景象,实际销售收入达到了令人瞩目的 2563.8 亿元&#x…...
会话跟踪方案:Cookie Session Token
什么是会话技术? Cookie 以登录为例,用户在浏览器中将账号密码输入并勾选自动登录,浏览器发送请求,请求头中设置Cookie:userName:张三 ,password:1234aa ,若登录成功,服务器将这个cookie保存…...
jemeter压力测试入门
1. 安装jemeter的压缩包并且解压 点击运行 2. 添加线程组 3. 线程组的参数设置 4. 添加http请求 5. 填写请求信息 添加监听器——结果树(结果),聚合报告(吞吐量报告) 6. 通过cvs数据文件设置,配置元件&…...
SpringBoot3 简单集成 Spring AI 并使用
文章目录 准备JDK17api key 创建项目编写配置文件创建controller启动并测试角色预设流式响应\异步响应ChatModel(聊天模型)ImageModel(文生图)文生语音语言翻译多模态Function Calling (函数调用第三方API)…...
【C/C++】程序设计基础知识(数据类型与表达式、控制语句、数组与结构)
【C/C】程序设计基础知识(数据类型与表达式、控制语句、数组与结构) 一、数据类型与表达式1.1C语言符号1.2C语言运算符1.3数据类型1.4常量与变量1.5基本运算1.6优先级和结合性1.7输入与输出 二、控制语句2.1顺序结构2.2选择结构2.3循环结构2.4break,cont…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
