AI 刷题 小M的蛋糕切割问题 题解 | 豆包MarsCode AI刷题

108 阅读6分钟

一、小M的蛋糕切割问题

1、问题描述

小M有一个 n×m 的矩形蛋糕,蛋糕被分为 n×m 个区域,每个区域都有一个美味度。她希望通过一刀将蛋糕切成两部分,一部分自己吃,另一部分给小团。切下的区域必须是完整的,即不能把某个小正方形切成两个小区域。

小M希望两个人吃的部分的美味度之和尽量接近。你的任务是计算出美味度差的最小值,即∣s1​−s2​∣ 的最小值,其中 s1​ 是小M吃到的美味度,s2​ 是小团吃到的美味度。

2、测试样例

样例1:

输入:n = 2, m = 3, a = [[1, 1, 4], [5, 1, 4]]
输出:0

样例2:

输入:n = 3, m = 3, a = [[3, 2, 1], [4, 5, 6], [7, 8, 9]]
输出:3

样例3:

输入:n = 2, m = 2, a = [[1, 2], [3, 4]]
输出:2

二、问题分析

这个问题是一个典型的二维数组问题,我们需要找到一种切割方式,使得切割后的两部分美味度之和的差值最小。这个问题可以通过动态规划或者贪心算法来解决,但这里我将采用一种更简单的方法进行问题的解答,即遍历所有可能的切割线。

1、解题思路

  • 计算总和:首先计算整个蛋糕的美味度总和。

  •  遍历所有可能的切割线:遍历所有可能的水平和垂直切割线。

  •  计算每部分的美味度:对于每条切割线,计算切割后两部分的美味度总和。

  •  计算差值并更新最小差值:计算两部分美味度总和的差值,并更新最小差值。

2、解题步骤

1. 初始化最小差值为无穷大。

2. 遍历所有可能的水平切割线:

  • 计算切割线以上的美味度总和。
  • 计算切割线以下的美味度总和。
  • 计算两部分的差值,并与当前最小差值比较,更新最小差值。

3. 遍历所有可能的垂直切割线:

  • 计算切割线左侧的美味度总和。
  • 计算切割线右侧的美味度总和。
  • 计算两部分的差值,并与当前最小差值比较,更新最小差值。

4. 返回最小差值。

三、代码实现

import java.util.Arrays;

public class Main {
    public static int solution(int n, int m, int[][] a) {
        // 计算整个蛋糕的美味度总和
        int totalSum = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                totalSum += a[i][j];
            }
        }

        int minDifference = Integer.MAX_VALUE;

        // 遍历所有可能的水平切割线
        for (int i = 1; i < n; i++) {
            int sum1 = 0;
            // 计算上半部分的美味度总和
            for (int row = 0; row < i; row++) {
                for (int col = 0; col < m; col++) {
                    sum1 += a[row][col];
                }
            }
            int sum2 = totalSum - sum1;
            int difference = Math.abs(sum1 - sum2);
            minDifference = Math.min(minDifference, difference);
        }

        // 遍历所有可能的垂直切割线
        for (int j = 1; j < m; j++) {
            int sum1 = 0;
            // 计算左半部分的美味度总和
            for (int col = 0; col < j; col++) {
                for (int row = 0; row < n; row++) {
                    sum1 += a[row][col];
                }
            }
            int sum2 = totalSum - sum1;
            int difference = Math.abs(sum1 - sum2);
            minDifference = Math.min(minDifference, difference);
        }

        return minDifference;
    }

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

四、复杂度分析

1、时间复杂度分析

  • 计算总和的时间复杂度为 O(nm)。
  • 遍历所有可能的水平切割线的时间复杂度为 O(nm)。
  • 遍历所有可能的垂直切割线的时间复杂度为 O(nm)。

因此,总的时间复杂度为 O(nm + nm + nm) = O(nm)。

2、空间复杂度分析

  • 除了输入的二维数组外,我们只需要几个额外的变量来存储总和和最小差值,所以空间复杂度为 O(1)。

五、总结

小M的蛋糕切割问题是一个典型的优化问题,通过枚举所有可能的切割线来寻找最优解。这个问题的分析和解答过程展示了如何使用简单的遍历和累加操作来找到最小差值,同时也从侧面说明了在算法设计中考虑边界情况和优化计算过程的重要性。通过这个问题,相信我们都能够学习到如何处理二维数据结构,并且加深了对算法复杂度分析的理解。

优化思考

虽然当前的解法简单,但并不是最优解,我们可以进一步优化,例如使用动态规划来减少重复计算,或者使用更高级的数据结构来存储和处理美味度信息。下面是代码优化的一些思路,感兴趣的同学可以参考一下~

  •  动态规划(DP): 动态规划是一种通过将复杂问题分解为更简单的子问题来解决的方法。对于这个问题,我们可以考虑使用动态规划来存储不同区域的美味度总和,从而避免在每次遍历时重复计算相同区域的总和。这样,我们可以更快地找到最小差值。
  •  前缀和: 使用前缀和数组可以快速计算出任意区域的美味度总和。对于水平切割,我们可以创建一个前缀和数组,其中每个元素代表到当前行为止的美味度总和。这样,对于任意两行之间的区域,我们可以直接通过前缀和数组计算出其美味度总和,而不需要每次都遍历这些行。
  •  贪心算法: 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是最好或最优的算法。对于这个问题,我们还可以尝试使用贪心算法来选择切割线,即在每一步都选择当前能够使得两部分美味度之和最接近的切割线。
  •  更高级的数据结构: 可以考虑使用线段树或树状数组这样的数据结构来更高效地更新和查询区域的美味度总和。这些数据结构可以支持在对数时间内的更新和查询操作,这对于处理大型数据集时特别有用。
  •  二分查找: 由于问题要求找到最小差值,我们可以使用二分查找来确定切割点。首先,我们计算出整个蛋糕的美味度总和,然后使用二分查找来确定切割点,使得两部分的美味度之和尽可能接近总和的一半。

总之,通过这些优化思路和方法,我们可以提高算法的效率,减少计算时间,特别是在处理大型蛋糕(即大尺寸的二维数组)时,这些优化方法可以帮助我们更快地找到问题的解决方案。