倪文迪陪你学蓝桥杯2021寒假每日一题:1.20日(2018省赛A组第8题)

2021年寒假每日一题,2017~2019年的省赛真题。
本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供。
后面的每日一题,每题发一个新博文,请大家每天博客蓝桥杯专栏: https://blog.csdn.net/weixin_43914593/category_10721247.html

提供C++、Java、Python三种语言的代码。

2018省赛A组第8题“全球变暖” ,题目链接:
http://oj.ecustacm.cn/problem.php?id=1365
https://www.dotcpp.com/oj/problem2276.html

1、题目描述


你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:

.......
.##....
.##....
....##.
..####.
...###.
.......

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。
具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:

.......
.......
.......
.......
....#..
.......
.......

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入:第一行包含一个整数N。 (1 <= N <= 1000) 。以下N行N列代表一张海域照片。 照片保证第1行、第1列、第N行、第N列的像素都是海洋。
输出:一个整数表示答案。


2、题解

   题目一看就是“连通块问题”,是基础搜索。用DFS或BFS都行:遍历一个连通块(找到这个连通块中所有的’#‘,并标记已经搜过,不用再搜);再遍历下一个连通块…;遍历完所有连通块,统计有多少个连通块。

  回到题目,什么岛屿不会被完全淹没?若岛中有个陆地(称为高地),它周围都是陆地,那么这个岛不会被完全淹没。
  用DFS或BFS搜出有多少个岛(连通块),并且在搜索时统计那些没有高地的岛(连通块)的数量,就是答案。
  因为每个像素点只用搜一次且必须搜一次,所以复杂度是 O ( N 2 ) O(N^2) O(N2)的,不可能更好了。
  下面用C++代码实现DFS,用Python实现BFS,并附上Java代码。

3、DFS的C++代码

  DFS所有的点,若遇到’#‘,就继续DFS这个’#‘周围的’#‘。把搜过的’#‘标记为已经搜过,不用再搜。标记那些没有高地的岛的数量,就是答案。

#include<bits/stdc++.h>
using namespace std;

int n;
char a[1010][1010]; //地图
int vis[1010][1010]={0};  //标记是否搜过
int d[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}}; //四个方向
int flag;  //用于标记这个岛中是否被完全淹没
void dfs(int x, int y){
    vis[x][y] = 1;      //标记这个'#'被搜过。注意为什么可以放在这里
    if(a[x][y+1]=='#' && a[x][y-1]=='#' && a[x+1][y]=='#' && a[x-1][y]=='#')
        flag = 1;       //上下左右都是陆地,不会淹没
    for(int i = 0; i < 4; i++){ //继续DFS周围的陆地
        int nx = x + d[i][0], ny = y + d[i][1];
      //if(nx>=1 && nx<=n && ny>=1 && ny<=n && vis[nx][ny]==0 && a[nx][ny]=='#') //题目说边上都是水,所以不用这么写了
        if(vis[nx][ny]==0 && a[nx][ny]=='#') //继续DFS未搜过的陆地,目的是标记它们
            dfs(nx,ny);
    }
}

int main(){
    cin >> n;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            cin >> a[i][j];
    int ans = 0 ;
    for(int i = 1; i <= n; i++)  //DFS所有像素点
        for(int j = 1; j <= n; j++)
            if(a[i][j]=='#' && vis[i][j]==0){
                flag = 0;
                dfs(i,j);
                if(flag == 0)  //这个岛全部被淹
                    ans++;     //统计岛的数量
            }
    cout<<ans<<endl;
    return 0;
}

4、BFS的Python代码

  下面的代码实现了BFS。注意Python有两种队列实现方式:
  (1)用queue实现队列操作,见下面的代码。
  (2)Python的list就是一个普通队列,代码中的注释是用list实现的队列。

from queue import *

def bfs(x,y):
    dirt=[(0,1),(0,-1),(1,0),(-1,0)]    
    q = Queue()               #q = [(x,y)]   #也可以用list来实现队列
    q.put((x,y))
    vis[x][y]=1
    flag = 0    
    
    while not q.empty():      #while len(q)!=0:        
        current=q.get()       #current=q.pop(0)
        x,y=current[0],current[1]
        if flag == 0:
          if a[x][y+1]=='#' and a[x][y-1]=='#' and a[x+1][y]=='#' and a[x-1][y]=='#':
             flag = 1                             #这个岛不淹没 
        
        for i in range(4):
            newx=x+dirt[i][0]
            newy=y+dirt[i][1]
            if vis[newx][newy]==0 and a[newx][newy]=="#":                              
               q.put((newx,newy))   #q.append((newx,newy))
               vis[newx][newy]=1                  #注意为什么放在这里
    if flag == 0:                                 #这个岛被淹
        return True
    return False                                  #这个岛不淹没
n=int(input())
a=[]
for i in range(n):
    a.append(list(input()))
vis=[]
for i in range(n):
    vis.append([0]*n) 
ans=0
for i in range(n):
    for j in range(n):
        if vis[i][j]==0 and a[i][j]=="#":  
            if bfs(i,j):   #这个岛被淹
                ans+=1
print(ans)

5、Java代码

  下面的Java代码实现了BFS。

//http://oj.ecustacm.cn/ User: 1840131230
import java.math.BigInteger;
import java.util.*;
public class Main 
{
    static class node{
        int x,y;
        node(int x,int y){
            this.x=x;
            this.y=y;
        }
    }
    static int n,sums=0;
    static char map[][];
    static int vist[][];
    static int dx[]= {0,0,-1,1};
    static int dy[]= {-1,1,0,0};
    static void bfs(int x,int y) {
        Queue<node> que=new LinkedList<node>();
        que.add(new node(x,y));
        vist[x][y]=1;
        boolean flag=true;
        while(!que.isEmpty()) {
            node p=que.remove();
            int xx=p.x;
            int yy=p.y;
            int sum=0;
            for(int i=0;i<4;i++) {
                int xxx=xx+dx[i];
                int yyy=yy+dy[i];
                if(xxx>=0&&xxx<n&&yyy>=0&&yyy<n&&map[xxx][yyy]=='#') {
                    sum++;
                }
            }
            if(sum==4)flag=false;
            for(int i=0;i<4;i++) {
                int xxx=xx+dx[i];
                int yyy=yy+dy[i];
                if(xxx>=0&&xxx<n&&yyy>=0&&yyy<n&&vist[xxx][yyy]==0&&map[xxx][yyy]=='#') {
                    vist[xxx][yyy]=1;
                    que.add(new node(xxx,yyy));
                    sum++;
                }
            }
             
        }
        if(flag)sums++;
    }
    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        map=new char[n][n];
        vist=new int[n][n];
        for(int i=0;i<n;i++) {
            map[i]=sc.next().toCharArray();
        }
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++) {
                if(map[i][j]=='#'&&vist[i][j]==0) {
                    bfs(i,j);
                }
            }
        }
        System.out.println(sums);
    }
} 
  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗勇军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值