104.建造最大岛屿
卡码网题目链接(ACM模式)(opens new window)
题目描述:
给定一个由 1(陆地)和 0(水)组成的矩阵,你最多可以将矩阵中的一格水变为一块陆地,在执行了此操作之后,矩阵中最大的岛屿面积是多少。
岛屿面积的计算方式为组成岛屿的陆地的总数。岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设矩阵外均被水包围。
输入描述:
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。
输出描述:
输出一个整数,表示最大的岛屿面积。如果矩阵中不存在岛屿,则输出 0。
分析:
本题的一个暴力想法,应该是遍历地图尝试 将每一个 0 改成1,然后去搜索地图中的最大的岛屿面积。
计算地图的最大面积:遍历地图 + 深搜岛屿,时间复杂度为 n * n。
(其实使用深搜还是广搜都是可以的,其目的就是遍历岛屿做一个标记,相当于染色,那么使用哪个遍历方式都行,以下我用深搜来讲解)
每改变一个0的方格,都需要重新计算一个地图的最大面积,所以 整体时间复杂度为:n^4。
优化思路
其实每次深搜遍历计算最大岛屿面积,我们都做了很多重复的工作。
只要用一次深搜把每个岛屿的面积记录下来就好。
第一步:一次遍历地图,得出各个岛屿的面积,并做编号记录。可以使用map记录,key为岛屿编号,value为岛屿面积
第二步:再遍历地图,遍历0的方格(因为要将0变成1),并统计该1(由0变成的1)周边岛屿面积,将其相邻面积相加在一起,遍历所有 0 之后,就可以得出 选一个0变成1 之后的最大面积。
拿如下地图的岛屿情况来举例: (1为陆地)
第一步,则遍历题目,并将岛屿到编号和面积上的统计,过程如图所示:
这个过程时间复杂度 n * n 。
相当于对整个矩阵深搜一遍:n*n(之前是对每个结点改变的情况,都要对整个矩阵进行深搜n^4)
再遍历空白位置:n*n
n * n这个方格地图中,每个节点我们就遍历一次,并不会重复遍历。
第二步过程如图所示:
也就是遍历每一个0的方格,并统计其相邻岛屿面积,最后取一个最大值。
这个过程的时间复杂度也为 n * n。
所以整个解法的时间复杂度,为 n * n + n * n 也就是 n^2。
可以优化掉visit函数,第一个岛编号从2开始就行了,也就是已经遇到过的陆地编号不会是1,更不会是0
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
int this_size,zonal_size;
int s=2;
int move[4][2]={1,0,0,1,-1,0,0,-1};
void dfs(int i,int j,int**box,int m,int n){
box[i][j]=s;
this_size++;
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 && box[movei][movej]==1) dfs(movei,movej,box,m,n);
}
}
void dfs2(int i,int j,int **box,int m,int n,int *copysize){//i,j附近的有编号的内容,然后加起来,然后返回
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 && box[movei][movej]!=0 && copysize[box[movei][movej]]!=0){
zonal_size+= copysize[box[movei][movej]];//标记这个面积已经使用过了
copysize[box[movei][movej]]=0;
dfs2(movei,movej,box,m,n,copysize);
}
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int x=m*n/2+3;
int** box=(int**)malloc(sizeof(int*)*n);
int *size = (int*)malloc(sizeof(int)*x);
for (int i=0;i<n;i++){
box[i]=(int*)malloc(sizeof(int)*m);
for(int j=0;j<m;j++) scanf("%d",&box[i][j]);
}
for(int i=0;i<x;i++) size[i]=0;
int ansmax=0;
for (int i=0;i<n;i++){//统计各个岛的面积
for(int j=0;j<m;j++){
if(box[i][j]==1){//=1是未访问过的
this_size=0;
dfs(i,j,box,m,n);
size[s]=this_size;
s++;
}
}
}
for(int i=2;i<s;i++) ansmax=fmax(size[i],ansmax);
int *copysize=(int *)malloc(sizeof(int)*x);
memcpy(copysize,size,sizeof(int)*x);
for (int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(box[i][j]==0){
zonal_size=1;
memcpy(copysize,size,sizeof(int)*x);
dfs2(i,j,box,m,n,copysize);
ansmax=fmax(ansmax,zonal_size);
}
}
}
printf("%d",ansmax);
return 0;
}
105.有向图的完全可达性
卡码网题目链接(ACM模式)(opens new window)
【题目描述】
给定一个有向图,包含 N 个节点,节点编号分别为 1,2,...,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。
【输入描述】
第一行包含两个正整数,表示节点数量 N 和边的数量 K。 后续 K 行,每行两个正整数 s 和 t,表示从 s 节点有一条边单向连接到 t 节点。
【输出描述】
如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。
分析:
1、用邻接表处理
2、实际上就是从1开始搜索,能到达的节点都用visit标为1
3、搜索完毕,如果有visit是0,那么就print -1;其他情况print 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
int visited[100];
//int record[100];
typedef struct arcnode{
int node;
struct arcnode * nextnode;
}Arcnode;
typedef struct vnode{
Arcnode* firstarc;
}Vnode;
typedef struct {
Vnode list[100];
}graph;
void dfs(int x,graph *g){
visited[x]=1;
Arcnode*p=g->list[x].firstarc;
while(p!=NULL){
int w=p->node;
if(visited[w]==0){
dfs(w,g);
}
p=p->nextnode;
}
}
int main(){
int n,k;
scanf("%d%d",&n,&k);
graph *g =(graph*)malloc(sizeof(graph));
for(int i=1;i<=n;i++){
g->list[i].firstarc=NULL;
}
for(int i=0;i<k;i++){
int s,t;
scanf("%d%d",&s,&t);
Arcnode* zzz=(Arcnode*)malloc(sizeof(Arcnode));
zzz->node=t;
zzz->nextnode=g->list[s].firstarc;
g->list[s].firstarc=zzz;
}
dfs(1,g);
for(int i=1;i<=n;i++){
if(visited[i]==0) {
printf("0");
return 0;
}
}
printf("1");
return 0;
}
bfs:
int queue[1000];
int front=-1,rear=-1;
void bfs(int x,graph*g){
queue[++rear]=x;
visited[x]=1;
while(front!=rear){
front++;
int xxx=queue[front];
Arcnode*p=g->list[xxx].firstarc;
while(p!=NULL){
int w=p->node;
if(visited[w]==0){
queue[++rear]=w;
visited[w]=1;
}
p=p->nextnode;
}
}
}
106. 岛屿的周长
卡码网题目链接(ACM模式)(opens new window)
题目描述
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。
你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿,假设组成岛屿的陆地边长都为 1,请计算岛屿的周长。岛屿内部没有水域。
输入描述
第一行包含两个整数 N, M,表示矩阵的行数和列数。之后 N 行,每行包含 M 个数字,数字为 1 或者 0,表示岛屿的单元格。
输出描述
输出一个整数,表示岛屿的周长。
分析:
1、涉及到周围,附近——用bfs
2、一个陆地,有四条边,每和一个陆地相邻 就需要-1(不考虑是否visit);但是bfs遍历的过程中 需要visit
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
int rear=-1,front=-1;
int queue[100][100];
int c=0;
int count=0;
int move[4][2]={1,0,0,1,-1,0,0,-1};
void bfs(int**box,int i,int j,int n,int m,int **visited){
rear++;
queue[rear][0]=i;
queue[rear][1]=j;
visited[i][j]=1;
while(front!=rear){
front++;
int xi = queue[front][0];
int xj = queue[front][1];//相当于是在出队的时候,操作了
count=4;
for(int k=0;k<4;k++){
int final_i=xi+move[k][0];
int final_j=xj+move[k][1];
if(final_i>=0 && final_i<n && final_j>=0 && final_j<m && box[final_i][final_j]==1) count--;
if(final_i>=0 && final_i<n && final_j>=0 && final_j<m && box[final_i][final_j]==1 && visited[final_i][final_j]==0 ){
rear++;
queue[rear][0]=final_i;
queue[rear][1]=final_j;
visited[final_i][final_j]=1;
}
//else if(final_i>=0 && final_i<n && final_j>=0 && final_j<m && box[final_i][final_j]==0) count++;
}
c=c+count;
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
int **box=(int**)malloc(sizeof(int*)*n);
int **visited=(int**)malloc(sizeof(int*)*n);
for(int i=0;i<n;i++){
box[i]=(int*)malloc(sizeof(int)*m);
visited[i]=(int*)malloc(sizeof(int)*m);
for(int j=0;j<m;j++){
scanf("%d",&box[i][j]);
visited[i][j]=0;
}
}
int p,q;
int flag=1;
for(int i=0;i<n && flag==1;i++){
for(int j=0;j<m;j++){
if(box[i][j]==1) {
p=i;
q=j;
flag=0;
break;
}
}
}
bfs(box,p,q,n,m,visited);
printf("%d",c);
return 0;
}
实际上不用bfs也行:
计算出总的岛屿数量,总的变数为:岛屿数量 * 4
因为有一对相邻两个陆地,边的总数就要减2,如图红线部分,有两个陆地相邻,总边数就要减2
那么只需要在计算出相邻岛屿的数量就可以了,相邻岛屿数量为cover。
结果 result = 岛屿数量 * 4 - cover * 2;