小R的蛋糕分享 | 豆包MarsCode AI刷题

159 阅读4分钟

题目总结笔记

问题描述

小R有一个大小为 nm 列的矩形蛋糕,每个小正方形区域都有一个代表美味度的整数。小R打算从中切割出一个正方形的小蛋糕给自己,而剩下的部分将给小S。她希望两人吃的部分的美味度之和尽量接近。

定义:

  • 小R吃到的部分的美味度之和为 s_1,小S吃到的部分的美味度之和为 s_2
  • 目标是通过选择一个正方形区域使得 |s_1 - s_2| 最小,即使得两部分的美味度差尽可能小。

输入输出

  • 输入

    • n:矩阵的行数,表示蛋糕的行数。
    • m:矩阵的列数,表示蛋糕的列数。
    • a:一个 nm 列的二维数组,表示蛋糕的美味度。
  • 输出

    • 一个整数,表示切割后的两部分美味度差的最小值。

示例

样例1:

输入:

n = 3, m = 3, a = [[1, 2, 3], [2, 3, 4], [3, 2, 1]]

输出:

1

样例2:

输入:

n = 4, m = 4, a = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 2, 3, 4], [4, 3, 2, 1]]

输出:

2

样例3:

输入:

n = 2, m = 2, a = [[5, 5], [5, 5]]

输出:

10

解题思路

  1. 问题理解

    • 题目要求我们从一个矩阵中选择一个正方形区域,并使得这部分和剩下部分的美味度差最小。目标是最小化两部分的美味度差,即 |s1 - s2|,其中 s1 是切割出的正方形部分的美味度,s2 是剩余部分的美味度。
  2. 总美味度的计算

    • 首先,我们需要计算整个矩阵的美味度之和 total_sum,即矩阵所有元素的和。
  3. 正方形区域选择

    • 正方形的大小可以从 1x1min(n, m)xmin(n, m),我们需要遍历所有可能的正方形区域,计算每个正方形区域的美味度 s1,并计算剩余部分的美味度 s2 = total_sum - s1
  4. 计算美味度差

    • 对于每个正方形区域,我们计算其美味度差 |2*s1 - total_sum|,并更新最小的美味度差。
  5. 优化计算

    • 为了高效地计算不同位置的正方形区域的美味度之和,我们可以使用 二维前缀和(二维累加和)来快速计算任意子矩阵的和。
    • 前缀和数组 prefixSum[i][j] 表示从矩阵的 (0, 0)(i-1, j-1) 区域的美味度之和。这样可以在常数时间内计算任意矩形区域的和。

Java代码实现

public class Main {
    public static int solution(int n, int m, int[][] a) {
        int total_sum = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                total_sum += a[i][j];
            }
        }
        
        int[][] prefixSum = new int[n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                prefixSum[i][j] = a[i - 1][j - 1] 
                                  + prefixSum[i - 1][j] 
                                  + prefixSum[i][j - 1] 
                                  - prefixSum[i - 1][j - 1];
            }
        }
        
        int minDiff = Integer.MAX_VALUE;
        
        for (int size = 1; size <= Math.min(n, m); size++) {
            for (int i = 0; i <= n - size; i++) {
                for (int j = 0; j <= m - size; j++) {
                    int s1 = prefixSum[i + size][j + size] 
                           - prefixSum[i][j + size] 
                           - prefixSum[i + size][j] 
                           + prefixSum[i][j];
                    
                    int diff = Math.abs(2 * s1 - total_sum);
                    minDiff = Math.min(minDiff, diff);
                }
            }
        }
        
        return minDiff;
    }

    public static void main(String[] args) {
        System.out.println(solution(3, 3, new int[][]{{1, 2, 3}, {2, 3, 4}, {3, 2, 1}}) == 1);
        System.out.println(solution(4, 4, new int[][]{{1, 2, 3, 4}, {4, 3, 2, 1}, {1, 2, 3, 4}, {4, 3, 2, 1}}) == 2);
        System.out.println(solution(2, 2, new int[][]{{5, 5}, {5, 5}}) == 10);
    }
}

解题步骤和思路总结

  1. 计算总美味度:首先计算整个矩阵的美味度总和。
  2. 二维前缀和:通过二维前缀和数组,快速计算任意子矩阵的美味度之和。
  3. 遍历所有正方形:从最小的正方形开始,遍历所有可能的正方形区域,计算其美味度之和,并更新最小的差值。
  4. 返回结果:最终返回最小的美味度差。

时间复杂度分析

  • 时间复杂度

    • 计算总美味度:O(n * m)
    • 计算二维前缀和:O(n * m)
    • 遍历所有可能的正方形区域:O(min(n, m) * n * m),最坏情况下可能遍历所有正方形。

    所以,总时间复杂度大致为:O(n * m * min(n, m))

  • 空间复杂度

    • 使用二维前缀和数组,空间复杂度为 O(n * m)

结论

通过二维前缀和优化,可以高效地求解矩阵中所有可能的正方形区域的美味度差,并得到最优的切割方案。