蓝桥杯--扩散

小蓝在一张无限大的特殊画布上作画。
这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。
小蓝在画布上首先点了一下几个点:(0, 0), (2020, 11), (11, 14), (2000, 2000)。
只有这几个格子上有黑色,其它位置都是白色的。
每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它
就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色
(如果原来就是黑色,则还是黑色)。
请问,经过 2020 分钟后,画布上有多少个格子是黑色的。

正确答案为:20312088


首先,这一题我们可以很明确的知道考察的是广度优先搜索,但是咋一看,因为这个画布没有给出范围,这时候就没有办法来通过数组来判断相邻的格子中,有哪些是访问过了
所以我一开始的思路是通过HashSet来判断是否已经有重复的格子,而问题来了,如果使用的是HashSet来判断的话,里面的泛型应该是什么类型呢
是一个数组吗?答案是否定的,因为如果我们定义HashSet的泛型是一个数组的话,那么这时候在添加相邻格子的时候,必然是需要通过new来新建的,这时候根据HashSet去重的原理,HashSet的底层数据结构是HashMap,而HashMap中添加元素put方法,会调用的是putVal方法。请看下面的图片:
在这里插入图片描述
所以这时候即使元素相同,如果地址不同,那么也会hashset同样会将其添加到set中,如下面的例子:

所以这时候我们应该定义的是一个类Point,表示格子的位置,所以需要有x,y这两个下标,同时为了方便set去重,Point类需要重写hashCode和equals方法才可以。

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

/*
小蓝在一张无限大的特殊画布上作画。
这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。
小蓝在画布上首先点了一下几个点:(0, 0), (2020, 11), (11, 14), (2000, 2000)。
只有这几个格子上有黑色,其它位置都是白色的。
每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它
就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色
(如果原来就是黑色,则还是黑色)。
请问,经过 2020 分钟后,画布上有多少个格子是黑色的。
 */
public class Test2 {
    public static void main(String[] args) {
        HashSet<Point> set = new HashSet<>();
        set.add(new Point(1,1));
        set.add(new Point(1,1));
        System.out.println(set.size());//输出为1
    }
}
class Point{
    int x;
    int y;
    public Point(int x,int y){
        this.x = x;
        this.y = y;
    }

    @Override
    public int hashCode() {
        return this.x + this.y;
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        }
        if(obj == null){
            return false;
        }
        if(obj.getClass() != this.getClass()){
            return false;
        }
        Point other = (Point)obj;
        return this.x == other.x && this.y == other.y;
    }
}

所以基于上面去重,所以我们利用bfs的代码就可以开始书写了:

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

public class Test2 {
    static int[] move_x = {-1,1,0,0};
    static int[] move_y = {0,0,-1,1};
    public static void main(String[] args) {
        HashSet<Point> set = new HashSet<>();
        Queue<Point> queue = new LinkedList<>();
        int[][] array = {{0, 0}, {2020, 11}, {11, 14}, {2000, 2000}};
        Point point;
        for (int[] arr : array) {
            point = new Point(arr[0],arr[1]);
            queue.offer(point);
            set.add(point);
        }
        int ans = 4,x,y;//x,y表示cur的横纵坐标
        int size ,t = 0;//t分钟的格子,将(t + 1)分钟的格子添加到队列中,所以下面应该判断为t < 2020
        Point cur;
        while (t < 2020 && !queue.isEmpty()){
            size = queue.size();//t分钟的格子
            for(int i = 0; i < size; ++i){
                cur = queue.poll();
                x = cur.getX();
                y = cur.getY();
                for(int k = 0; k < 4; ++k){
                    //判断将当前格子相邻的4个格子是否都可以添加到队列中
                    int next_x = x + move_x[k];
                    int next_y = y + move_y[k];
                    //System.out.println(x + ", " + y + ", " + next_x + ", " + next_y);
                    point = new Point(next_x,next_y);
                    if(!set.contains(point)){
                        queue.offer(point);
                        set.add(point);
                        ++ans;
                    }
                }
            }
            ++t;
        }
        System.out.println(ans);
    }
}
class Point{
    int x;
    int y;
    public Point(int x,int y){
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public int hashCode() {
        return this.x + this.y;
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        }
        if(obj == null){
            return false;
        }
        if(obj.getClass() != this.getClass()){
            return false;
        }
        Point other = (Point)obj;
        return this.x == other.x && this.y == other.y;
    }
}

然而,真正运行的时候,运行的时间却是很长。。。
在这里插入图片描述
而主要耗费的就是在去重那里,这时候我们在仔细看题目,然后我们在来思考一下,给出了初始的点,并且已经给出了时间,那么这时候我们就可以知道最后2020分钟之后,在最边缘的格子的位置了,然后根据边缘格子的位置,来构造一个二维数组isVisited,这个二维数组用来标记哪一些格子时已经被访问过的

但是此时还没有可以,因为最边缘的格子的位置可能是负数,即它的横、纵坐标至少有一个时小于0的,那么这时候就会导致isVisited下标越界。这时候我们应该怎么办呢?我们只要将最左边的格子往右上角位置,使得最边缘的格子是在[0,0]位置即可。那么这时候题目中给出的几个点在isVisited中的位置也要相应的往右上角移动

基于上面的思路,我们的代码就改成了:

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

/*
小蓝在一张无限大的特殊画布上作画。
这张画布可以看成一个方格图,每个格子可以用一个二维的整数坐标表示。
小蓝在画布上首先点了一下几个点:(0, 0), (2020, 11), (11, 14), (2000, 2000)。
只有这几个格子上有黑色,其它位置都是白色的。
每过一分钟,黑色就会扩散一点。具体的,如果一个格子里面是黑色,它
就会扩散到上、下、左、右四个相邻的格子中,使得这四个格子也变成黑色
(如果原来就是黑色,则还是黑色)。
请问,经过 2020 分钟后,画布上有多少个格子是黑色的。
 */
public class Test2 {

    static int[] move_x = {-1,1,0,0};
    static int[] move_y = {0,0,-1,1};
    public static void main(String[] args) {
        Queue<int[]> queue = new LinkedList<>();
        int[][] array = {{0, 0}, {2020, 11}, {11, 14}, {2000, 2000}};
        /*
        2020分钟之后的4个边缘位置对应的横坐标或者纵坐标
        int top = 2000 + 2020; //最上方的格子的纵坐标,由(2020,2000)往上移动
        int bottom = 0 - 2020; //最下方的格子的纵坐标,由(0,0)往下移动
        int left = 0 - 2020;//最左边的格子的横坐标,由(0,0)往左移动
        int right = 2020 + 2020;//最右边的格子横坐标,由(2020,11)往右移动
         */
        boolean[][] isVisited = new boolean[10000][10000];
        for (int[] arr : array) {
           queue.offer(new int[]{arr[0] + 2020,arr[1] + 2020});//避免存在越界的情况,将下标往右上角移动
           isVisited[arr[0] + 2020][arr[1] + 2020] = true;
        }
        int[] cur;
        int ans = 4,t = 0,size;
        while (!queue.isEmpty() && t < 2020) {
            size = queue.size();
            for(int j = 0; j < size; ++j){
                cur = queue.poll();
                for(int i = 0; i < 4; ++i){
                    int next_x = cur[0] + move_x[i];
                    int next_y = cur[1] + move_y[i];
                    if(next_x < 0 || next_y < 0 || isVisited[next_x][next_y]){
                        continue;
                    }
                    queue.offer(new int[]{next_x,next_y});
                    isVisited[next_x][next_y] = true;
                    ++ans;
                }
            }
           ++t;
        }
        System.out.println(ans);
     }

}

输出为:
在这里插入图片描述

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值