代码随想录刷题记录(七)——44. 开发商购买土地

(一)问题描述

       这道题是代码随想录数组部分的补充题目,在卡码网上进行练习。使用ACM输入输出模式进行编码。

卡码网 44. 开发商购买土地(第五期模拟笔试)icon-default.png?t=O83Ahttps://kamacoder.com/problempage.php?pid=1044

        在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。 

       现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

       然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。 

       注意:区块不可再分。

输入描述:

       第一行输入两个正整数,代表 n 和 m。 

       接下来的 n 行,每行输出 m 个正整数。

输出描述:

        请输出一个整数,代表两个子区域内土地总价值之间的最小差距。

输入示例:

3 3
1 2 3
2 1 3
1 2 3

输出示例:

0

提示信息:

如果将区域按照如下方式划分:

1 2 | 3
2 1 | 3
1 2 | 3 

两个子区域内土地总价值之间的最小差距可以达到 0。

数据范围:

1 <= n, m <= 100;
n 和 m 不同时为 1。

(二)关键词提取

  • n*m连续区块,且区块不可再分,每个区块都有权值→大小为n*m且元素为整数的二维数组问题。
  • 划分为两个连续的子区域,求子区域和的差值的最小值→二位数组的区间求和问题→前缀和

 (三)解题方法

       我使用java解题!

1. 前缀和法

       分别按行和按列划分,求出各行/各列的元素和,形成前缀和数组,和上题58.区间和思路类似。这里要比较两部分的差值,假设划分后两部分分别是A和B,元素的总和是C,那么B-A=C-A-A=C-2A,C即前缀和数组的最后一个元素值,A(即前a各元素的和)也可以用前缀和数组的元素来表示。

import java.util.Scanner;

public class Main{
    public static void main(String args[]){
        Scanner scanner = new Scanner(System.in);
        
        
        int n=0,m=0;
        if(scanner.hasNextInt()){
            n=scanner.nextInt();
            m=scanner.nextInt();
        }
       
       
        int[][] area=new int[n][m];
        int[] linesum=new int[n];
        int[] colsum=new int[m];
        int tempsum=0;
        
        for(int i=0;i<n;i++){//读取数组,并计算每行的前缀和数组
            for(int j=0;j<m;j++){
                if(scanner.hasNextInt()){
                    area[i][j]=scanner.nextInt();
                    tempsum+=area[i][j];
                }
            }
             linesum[i]=tempsum;
        }
        
        int linemin=Integer.MAX_VALUE;//找到按行划分时C-2A的最小值
        for(int k=0;k<n;k++){
            if(linemin>Math.abs(linesum[n-1]-2*linesum[k])){
                linemin=Math.abs(linesum[n-1]-2*linesum[k]);
            }
        }
        
        tempsum=0;
         for(int j=0;j<m;j++){//计算每列的前缀和数组
            for(int i=0;i<n;i++){
                tempsum+=area[i][j];   
            }
            colsum[j]=tempsum;
        }
        
        int colmin=Integer.MAX_VALUE;//找到按列划分时C-2A的最小值
        for(int k=0;k<m;k++){
            if(colmin>Math.abs(colsum[m-1]-2*colsum[k])){
                colmin=Math.abs(colsum[m-1]-2*colsum[k]);
            }
        }
        
        int result=Math.min(linemin,colmin);//取按行划分和按列划分时的最小值为最终结果
        System.out.println(result);
    }
}

2.改进的暴力解法

       暴力解法分别计算划分后两个区域的总和并作差,不断移动分割线,找到最小的差值。此时时间复杂度是O(n^3),一个for循环枚举分隔线,两个for循环求区间的和。改进的暴力解法即用上述讲过的C-2A的方式来计算。先求出所有元素的和,累加一行(按行划分)或一列(按列划分)之后,得到新的A,在计算C-2A的值,直到找出最小值。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int sum = 0;
        int[][] vec = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                vec[i][j] = scanner.nextInt();
                sum += vec[i][j];
            }
        }

        int result = Integer.MAX_VALUE;
        int count = 0; // 统计遍历过的行

        // 行切分
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                count += vec[i][j];
                // 遍历到行末尾时候开始统计
                if (j == m - 1) {
                    result = Math.min(result, Math.abs(sum - 2 * count));
                }
            }
        }

        count = 0;
        // 列切分
        for (int j = 0; j < m; j++) {
            for (int i = 0; i < n; i++) {
                count += vec[i][j];
                // 遍历到列末尾时候开始统计
                if (i == n - 1) {
                    result = Math.min(result, Math.abs(sum - 2 * count));
                }
            }
        }

        System.out.println(result);
        scanner.close();
    }
}

(四)易错点

  • 涉及到的变量比较多,又是m又是n的,谁和谁比较别搞反了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值