代码随想录算法训练营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…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...