代码随想录算法训练营day53:图04:104.建造最大岛屿;110. 字符串接龙;105.有向图的完全可达性

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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值