蓝桥杯 历届试题 剪格子 java

Prev4 历届试题 剪格子

题目如下:
image-20200824171441829

这道题看似挺简单,能看出就是用dfs来搜索的,而且通过"包含左上角的分割区"这句话可以得知此次dfs应该以左上角为起点

而且可以从描述得到,我们搜索格子的结束标志就是搜索到的格子的权值等于所有格子权值的一半

那么就好写了,还要注意的就是我们搜索到的那一部分肯定是一部分的,但我们把这部分去掉之后,剩下的格子是不是还只有一部分?这是需要判断其的连通性的。但其实这道题数据很水,才三组,所以你不考虑这个也能过,但还是严谨点比较好,有同学想练连通性的题的话可以练第九届的全球变暖

还有就是几种特殊情况的,比如

4 4
2 1 1 2
2 2 1 1
2 2 2 1
1 1 1 2

这种按目前代码是算出来7,但其实按题目真正意义的答案却是6,这种递归回溯是算不出来的,我之后会学习一下再改一下本题

源码和注释如下

package prev;

import java.util.Arrays;
import java.util.Scanner;

/**
 * @Description: 历届试题 剪格子
 * @ClassName: Prev4
 * @author: fan.yang
 * @date: 2020/08/24 14:54
 */
public class Prev4 {

    private static int[][] map;

    private static int m;

    private static int n;
    //四个方向
    private static int[][] dir = {{-1 , 0},{1 , 0},{0 , -1},{0 , 1}};
    //访问数组
    private static int[][] visit;
    //判断连通性用到的访问拷贝数组
    private static int[][] visitCopy;

    private static int sum = 0;
    //先设个100 因为题目说m,n < 10 所以格子数不会大于100
    private static int minCount = 100;

    //连通性dfs
    //连通性的不回溯 因为我们的目的只是把能找的0都变成1
    public static void connected(int x, int y){
        for(int i = 0;i < 4;i++){
            int newX = x + dir[i][0];
            int newY = y + dir[i][1];
            if(newX < 0 || newY < 0 || newX >= n || newY >= m || visitCopy[newX][newY] == 1){
                continue;
            }
            visitCopy[newX][newY] = 1;
            connected(newX, newY);
        }
    }

    //dfs x,y是当前格子的坐标 k是当前搜索到的格子的权值和 count是格子数
    public static void dfs(int x, int y, int k, int count){
        //剪枝 当前搜索到的格子的权值和已经大于一半的情况下就没必要继续下去了
        if(k > sum / 2){
            return;
        }
        //如果满足条件
        if(k == sum / 2){
            //先判断剩下的权值的连通性
            visitCopy = Arrays.copyOf(visit, visit.length);
            for(int i = 0;i < n;i++){
                for(int j = 0;j < m;j++){
                    //找到第一个还没被访问过的点
                    if(visit[i][j] == 0){
                        //连通性dfs 从该店开始 把能连到的点都访问了
                        visitCopy[i][j] = 1;
                        connected(i, j);
                        //结束后如果还有0的点 那么代表剩下的权值至少分成了两部分
                        if(Arrays.deepToString(visitCopy).indexOf('0') != - 1){
                            return;
                        }
                    }
                }
            }
            //能到这里 就代表条件都满足
            if(minCount > count){
                minCount = count;
            }
            return;
        }
        //这个就是一个常规的递归回溯
        for(int i = 0;i < 4;i++){
            int newX = x + dir[i][0];
            int newY = y + dir[i][1];
            if(newX < 0 || newY < 0 || newX >= n || newY >= m || visit[newX][newY] == 1){
                continue;
            }
            visit[newX][newY] = 1;
            dfs(newX, newY, k + map[newX][newY], count + 1);
            visit[newX][newY] = 0;
        }

    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        m = scanner.nextInt();
        n = scanner.nextInt();
        map = new int[n][m];
        visit = new int[n][m];
        //随便求一下所有格子权值总和
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++){
                map[i][j] = scanner.nextInt();
                sum += map[i][j];
            }
        }
        //以[0,0]为起点 开始深搜
        visit[0][0] = 1;
        dfs(0, 0, map[0][0], 1);
        System.out.println(minCount == 100 ? 0 : minCount);
    }

}
//这个例子可以用来判断连通性
/*
4 3
1 3 1 9
2 3 4 5
2 2 14 6
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值