2234.花园的最大总美丽值

目标

Alice 是 n 个花园的园丁,她想通过种花,最大化她所有花园的总美丽值。

给你一个下标从 0 开始大小为 n 的整数数组 flowers ,其中 flowers[i] 是第 i 个花园里已经种的花的数目。已经种了的花 不能 移走。同时给你 newFlowers ,表示 Alice 额外可以种花的 最大数目 。同时给你的还有整数 target ,full 和 partial 。

如果一个花园有 至少 target 朵花,那么这个花园称为 完善的 ,花园的 总美丽值 为以下分数之 和 :

  • 完善 花园数目乘以 full.
  • 剩余 不完善 花园里,花的 最少数目 乘以 partial 。如果没有不完善花园,那么这一部分的值为 0 。

请你返回 Alice 种最多 newFlowers 朵花以后,能得到的 最大 总美丽值。

示例 1:

输入:flowers = [1,3,1,1], newFlowers = 7, target = 6, full = 12, partial = 1
输出:14
解释:Alice 可以按以下方案种花
- 在第 0 个花园种 2 朵花
- 在第 1 个花园种 3 朵花
- 在第 2 个花园种 1 朵花
- 在第 3 个花园种 1 朵花
花园里花的数目为 [3,6,2,2] 。总共种了 2 + 3 + 1 + 1 = 7 朵花。
只有 1 个花园是完善的。
不完善花园里花的最少数目是 2 。
所以总美丽值为 1 * 12 + 2 * 1 = 12 + 2 = 14 。
没有其他方案可以让花园总美丽值超过 14 。

示例 2:

输入:flowers = [2,4,5,3], newFlowers = 10, target = 5, full = 2, partial = 6
输出:30
解释:Alice 可以按以下方案种花
- 在第 0 个花园种 3 朵花
- 在第 1 个花园种 0 朵花
- 在第 2 个花园种 0 朵花
- 在第 3 个花园种 2 朵花
花园里花的数目为 [5,4,5,5] 。总共种了 3 + 0 + 0 + 2 = 5 朵花。
有 3 个花园是完善的。
不完善花园里花的最少数目为 4 。
所以总美丽值为 3 * 2 + 4 * 6 = 6 + 24 = 30 。
没有其他方案可以让花园总美丽值超过 30 。
注意,Alice可以让所有花园都变成完善的,但这样她的总美丽值反而更小。

说明:

  • 1 <= flowers.length <= 10^5
  • 1 <= flowers[i], target <= 10^5
  • 1 <= newFlowers <= 10^10
  • 1 <= full, partial <= 10^5

思路

有一个整数数组 flowersflowers[i] 表示下标为 i 的花园种植的花的数目。花园的美丽值通过以下方式计算:

  • 花园中花的数目大于等于 target,称该花园是完善的,美丽值 = 所有完善花园的个数 * full
  • 花园中花的数目小于 target,美丽值 = 所有花园中花的最小数目 * partial

Alice 可以向任意花园中种花,新种的花的数目不超过 newFlowers,求花园的最大美丽值。

// todo

代码

性能

1328.破坏回文串

目标

给你一个由小写英文字母组成的回文字符串 palindrome ,请你将其中 一个 字符用任意小写英文字母替换,使得结果字符串的 字典序最小 ,且 不是 回文串。

请你返回结果字符串。如果无法做到,则返回一个 空串 。

如果两个字符串长度相同,那么字符串 a 字典序比字符串 b 小可以这样定义:在 a 和 b 出现不同的第一个位置上,字符串 a 中的字符严格小于 b 中的对应字符。例如,"abcc” 字典序比 "abcd" 小,因为不同的第一个位置是在第四个字符,显然 'c' 比 'd' 小。

示例 1:

输入:palindrome = "abccba"
输出:"aaccba"
解释:存在多种方法可以使 "abccba" 不是回文,例如 "zbccba", "aaccba", 和 "abacba" 。
在所有方法中,"aaccba" 的字典序最小。

示例 2:

输入:palindrome = "a"
输出:""
解释:不存在替换一个字符使 "a" 变成非回文的方法,所以返回空字符串。

说明:

  • 1 <= palindrome.length <= 1000
  • palindrome 只包含小写英文字母。

思路

有一个回文字符串,可以修改其中一个字符使其不是回文,求修改后字典序最小的字符串。如果无法破会回文串,返回空串。

根据题意,字符串长度为 1 直接返回空串。

要使字典序最小,只需使字符串最左边的字符为 a,但是需考虑能否破坏回文串。于是就有了贪心解法,从左到右将对应位置上的字符修改为 a,判断是否是回文,不是回文直接返回。如果均为 a,将最后一个字符替换为 b

注意不要改中间的字符,它不会破坏回文串。

代码


/**
 * @date 2025-03-05 0:06
 */
public class BreakPalindrome1328 {

    public String breakPalindrome(String palindrome) {
        int n = palindrome.length();
        if (n == 1) {
            return "";
        }
        int l = 0, r = n - 1;
        char[] chars = palindrome.toCharArray();
        while (l < r) {
            if (chars[r] != 'a') {
                chars[l] = 'a';
                return new String(chars);
            }
            l++;
            r--;
        }
        chars[n - 1] = 'b';
        return new String(chars);
    }

}

性能

624.数组列表中的最大距离

目标

给定 m 个数组,每个数组都已经按照升序排好序了。

现在你需要从两个不同的数组中选择两个整数(每个数组选一个)并且计算它们的距离。两个整数 a 和 b 之间的距离定义为它们差的绝对值 |a-b| 。

返回最大距离。

示例 1:

输入:[[1,2,3],[4,5],[1,2,3]]
输出:4
解释:
一种得到答案 4 的方法是从第一个数组或者第三个数组中选择 1,同时从第二个数组中选择 5 。

示例 2:

输入:arrays = [[1],[1]]
输出:0

说明:

  • m == arrays.length
  • 2 <= m <= 10^5
  • 1 <= arrays[i].length <= 500
  • -10^4 <= arrays[i][j] <= 10^4
  • arrays[i] 以 升序 排序。
  • 所有数组中最多有 10^5 个整数。

思路

有一个二维数组,其中的每个数组都按升序排列。任选其中两个数组,从每个数组中选两个元素,求这两个元素距离的最大值,距离指差的绝对值。

显然应该选取最大值与最小值的差,但题目限制是从两个数组中取。我们可以求得最大与次最大、最小与次最小,同时记录所属数组。

如果有多个最值相同,从哪个数组中取有影响吗?由于我们从每个组只取首尾两个元素,最值与次最值一定来自不同的组,所以最值的比较可以使用 >=<=。如果最值来自相同的组,那么只需比较最大值与次最小值,次最大值与最小值。

网友题解使用变量记录前面的最大与最小值,那么最大距离为 Math.max(res, Math.max(curMax - preMin, preMax - curMin))

代码


/**
 * @date 2025-02-19 8:57
 */
public class MaxDistance624 {

    public int maxDistance_v1(List<List<Integer>> arrays) {
        int res = 0;
        int preMax = Integer.MIN_VALUE / 2;
        int preMin = Integer.MAX_VALUE / 2;
        for (List<Integer> array : arrays) {
            int n = array.size();
            Integer curMax = array.get(n - 1);
            Integer curMin = array.get(0);
            res = Math.max(res, Math.max(curMax - preMin, preMax - curMin));
            preMax = Math.max(preMax, curMax);
            preMin = Math.min(preMin, curMin);
        }
        return res;
    }

    public int maxDistance(List<List<Integer>> arrays) {
        int[] max = new int[]{Integer.MIN_VALUE, -1};
        int secondMax = Integer.MIN_VALUE;
        int[] min = new int[]{Integer.MAX_VALUE, -1};
        int secondMin = Integer.MAX_VALUE;
        for (int i = 0; i < arrays.size(); i++) {
            List<Integer> array = arrays.get(i);
            int n = array.size();
            if (min[0] >= array.get(0)) {
                secondMin = min[0];
                min[0] = array.get(0);
                min[1] = i;
            } else if (secondMin > array.get(0)) {
                secondMin = array.get(0);
            }
            if (max[0] <= array.get(n - 1)) {
                secondMax = max[0];
                max[0] = array.get(n - 1);
                max[1] = i;
            } else if (secondMax < array.get(n - 1)) {
                secondMax = array.get(n - 1);
            }
        }
        if (max[1] != min[1]) {
            return max[0] - min[0];
        } else {
            return Math.max(max[0] - secondMin, secondMax - min[0]);
        }
    }

}

性能

1552.两球之间的磁力

目标

在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n 个空的篮子,第 i 个篮子的位置在 position[i] ,Morty 想把 m 个球放到这些篮子里,使得任意两球间 最小磁力 最大。

已知两个球如果分别位于 x 和 y ,那么它们之间的磁力为 |x - y| 。

给你一个整数数组 position 和一个整数 m ,请你返回最大化的最小磁力。

示例 1:

输入:position = [1,2,3,4,7], m = 3
输出:3
解释:将 3 个球分别放入位于 1,4 和 7 的三个篮子,两球间的磁力分别为 [3, 3, 6]。最小磁力为 3 。我们没办法让最小磁力大于 3 。

示例 2:

输入:position = [5,4,3,2,1,1000000000], m = 2
输出:999999999
解释:我们使用位于 1 和 1000000000 的篮子时最小磁力最大。

说明:

  • n == position.length
  • 2 <= n <= 10^5
  • 1 <= position[i] <= 10^9
  • 所有 position 中的整数 互不相同 。
  • 2 <= m <= position.length

提示:

  • If you can place balls such that the answer is x then you can do it for y where y < x.
  • Similarly if you cannot place balls such that the answer is x then you can do it for y where y > x.
  • Binary search on the answer and greedily see if it is possible.

思路

m 个球放到 n 个空篮子中,任意两个球之间的磁力为 |position[a] - position[b]|,返回最大的磁力最小值。

看到最大化最小值就想到二分,n 个盒子放 m 个球,每个盒子至多放 1 个。假定最小磁力为 y,即相邻球的磁力最少为 y。将 position 排序,累加相邻元素的差即磁力,如果大于 y 则放置一个球,看能否放得下。

代码


/**
 * @date 2025-02-14 9:35
 */
public class MaxDistance1552 {

    public int maxDistance(int[] position, int m) {
        Arrays.sort(position);
        int n = position.length;
        int left = 0, right = position[n - 1] / (m - 1);
        int mid = left + (right - left) / 2;
        while (left <= right) {
            if (check(mid, position, m - 1)) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
            mid = left + (right - left) / 2;
        }
        return right;
    }

    public boolean check(int mid, int[] position, int m) {
        int start = position[0];
        for (int i = 1; i < position.length; i++) {
            if (position[i] >= start + mid) {
                if (--m == 0) {
                    return true;
                }
                start = position[i];
            }
        }
        return false;
    }

}

性能

1760.袋子里最少数目的球

目标

给你一个整数数组 nums ,其中 nums[i] 表示第 i 个袋子里球的数目。同时给你一个整数 maxOperations 。

你可以进行如下操作至多 maxOperations 次:

  • 选择任意一个袋子,并将袋子里的球分到 2 个新的袋子中,每个袋子里都有 正整数 个球。
  • 比方说,一个袋子里有 5 个球,你可以把它们分到两个新袋子里,分别有 1 个和 4 个球,或者分别有 2 个和 3 个球。

你的开销是单个袋子里球数目的 最大值 ,你想要 最小化 开销。

请你返回进行上述操作后的最小开销。

示例 1:

输入:nums = [9], maxOperations = 2
输出:3
解释:
- 将装有 9 个球的袋子分成装有 6 个和 3 个球的袋子。[9] -> [6,3] 。
- 将装有 6 个球的袋子分成装有 3 个和 3 个球的袋子。[6,3] -> [3,3,3] 。
装有最多球的袋子里装有 3 个球,所以开销为 3 并返回 3 。

示例 2:

输入:nums = [2,4,8,2], maxOperations = 4
输出:2
解释:
- 将装有 8 个球的袋子分成装有 4 个和 4 个球的袋子。[2,4,8,2] -> [2,4,4,4,2] 。
- 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,4,4,4,2] -> [2,2,2,4,4,2] 。
- 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,4,4,2] -> [2,2,2,2,2,4,2] 。
- 将装有 4 个球的袋子分成装有 2 个和 2 个球的袋子。[2,2,2,2,2,4,2] -> [2,2,2,2,2,2,2,2] 。
装有最多球的袋子里装有 2 个球,所以开销为 2 并返回 2 。

示例 3:

输入:nums = [7,17], maxOperations = 2
输出:7

说明:

1 <= nums.length <= 10^5
1 <= maxOperations, nums[i] <= 10^9

思路

n 个袋子,nums[i] 表示第 i + 1 个袋子中球的数目。每次操作可以任选一个袋子,将其中的球分成非空的两部分并装入两个 袋子。求执行 maxOperations 次操作后袋中球的数量最大值的最小值是多少。

考虑暴力解法,要最小化最大值,我们应该首先操作球最多的袋子,划分方案有 max / 2 种。这里需要枚举所有划分方案,然后重新计算球最多的袋子重复这一处理流程。最坏的情况下所有元素值都相等,时间复杂度为 O((max/2)^maxOperations)。这种解法显然行不通。

既然正向思维行不通,那就考虑逆向思维。假定一个最大值 mid,看能否在 maxOperations 次操作内将所有袋子种的球都降到 mid 以下。

将数字 num 拆分成不大于 mid 的数字最少需要操作几次?这里可以使用贪心思想,将 num 拆分为 num - midmid,然后接着拆分 num - mid 直到它小于等于 mid

关于次数的计算,如果 num % mid == 0,我们只需划分 num / mid - 1 次,因为剩余的部分为 mid。如果余数不为 0,则需要划分 num / mid 次。以上两种情况可以统一写成 (num - 1) / mid

代码


/**
 * @date 2025-02-12 0:03
 */
public class MinimumSize1760 {

    public int minimumSize(int[] nums, int maxOperations) {
        int left = 1, right = 1000000000;
        int mid = left + (right - left) / 2;
        while (left <= right) {
            if (check(nums, mid, maxOperations)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
            mid = left + (right - left) / 2;
        }
        return left;
    }

    public boolean check(int[] nums, int mid, int maxOperations) {
        for (int num : nums) {
            maxOperations -= (num - 1) / mid;
            if (maxOperations < 0) {
                return false;
            }
        }
        return true;
    }

}

性能

680.验证回文串II

目标

给你一个字符串 s,最多 可以从中删除一个字符。

请你判断 s 是否能成为回文字符串:如果能,返回 true ;否则,返回 false 。

示例 1:

输入:s = "aba"
输出:true

示例 2:

输入:s = "abca"
输出:true
解释:你可以删除字符 'c' 。

示例 3:

输入:s = "abc"
输出:false

说明:

  • 1 <= s.length <= 10^5
  • s 由小写英文字母组成

思路

判断给定字符串是否是回文,如果不是,能否删除任意一个字符使之变成回文。

当不满足回文条件时,分别考虑删掉其中一个字符,判断剩余子串是否是回文即可。

代码


/**
 * @date 2025-02-03 18:24
 */
public class ValidPalindrome680 {

    public boolean validPalindrome(String s) {
        int n = s.length();
        int i = 0;
        for (; i < n / 2; i++) {
            // 找到第一个不满足回文的字符下标
            if (s.charAt(i) != s.charAt(n - 1 - i)) {
                break;
            }
        }
        if (i == n / 2) {
            return true;
        }
        // 尝试删掉左边/右边字符判断剩余字符是否是回文
        boolean res = true;
        for (int j = i; j < n / 2; j++) {
            // 删掉 n - 1 - i,即 n - 2 - j
            if (s.charAt(j) != s.charAt(n - 2 - j)) {
                res = false;
            }
        }
        if (res) {
            return true;
        }
        // 这里是 j <= n /2,例如 abc,i + 1 指向 b
        for (int j = i + 1; j <= n / 2; j++) {
            // 这里是 n - 1 - i, 即 n - j,相当于删除了 i,但是右指针是不变的
            if (s.charAt(j) != s.charAt(n - j)) {
                return false;
            }
        }
        return true;
    }

}

性能

45.跳跃游戏II

目标

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i]
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

说明:

  • 1 <= nums.length <= 10^4
  • 0 <= nums[i] <= 1000
  • 题目保证可以到达 nums[n-1]

思路

数组 nums 的元素表示可以向后跳跃的最大长度,求从 0 跳到 n - 1 所需的最小跳跃次数。

定义 dp[i] 表示到达下标 i 所需的最小跳跃次数,状态转移方程为 dp[i + k] = min(dp[i] + 1),其中 k ∈ [1, nums[i]]

代码


/**
 * @date 2024-03-07 17:14
 */
public class CanJumpII45 {

    public int jump(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        Arrays.fill(dp, 0x3f3f3f);
        dp[0] = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n && j <= i + nums[i]; j++) {
                dp[j] = Math.min(dp[j], dp[i] + 1);
            }
        }
        return dp[n - 1];
    }

}

性能

2412.完成所有交易的初始最少钱数

目标

给你一个下标从 0 开始的二维整数数组 transactions,其中transactions[i] = [costi, cashbacki] 。

数组描述了若干笔交易。其中每笔交易必须以 某种顺序 恰好完成一次。在任意一个时刻,你有一定数目的钱 money ,为了完成交易 i ,money >= costi 这个条件必须为真。执行交易后,你的钱数 money 变成 money - costi + cashbacki 。

请你返回 任意一种 交易顺序下,你都能完成所有交易的最少钱数 money 是多少。

示例 1:

输入:transactions = [[2,1],[5,0],[4,2]]
输出:10
解释:
刚开始 money = 10 ,交易可以以任意顺序进行。
可以证明如果 money < 10 ,那么某些交易无法进行。

示例 2:

输入:transactions = [[3,0],[0,3]]
输出:3
解释:
- 如果交易执行的顺序是 [[3,0],[0,3]] ,完成所有交易需要的最少钱数是 3 。
- 如果交易执行的顺序是 [[0,3],[3,0]] ,完成所有交易需要的最少钱数是 0 。
所以,刚开始钱数为 3 ,任意顺序下交易都可以全部完成。

说明:

  • 1 <= transactions.length <= 10^5
  • transactions[i].length == 2
  • 0 <= costi, cashbacki <= 10^9

提示:

  • Split transactions that have cashback greater or equal to cost apart from transactions that have cashback less than cost. You will always earn money in the first scenario.
  • For transactions that have cashback greater or equal to cost, sort them by cost in descending order.
  • For transactions that have cashback less than cost, sort them by cashback in ascending order.

思路

有若干笔交易 transactionstransactions[i] = [costi, cashbacki] 表示每笔交易的现金支出为 costi,收入为 cashbacki。求以任意顺序完成交易所需的最少现金 money,完成交易 i 需要满足 money >= costi

注意题目求的是任意顺序下都能完成所有交易的最少现金数量,而不是求所有交易顺序中,能够保证完成所有交易的现金最小值。

看了提示,说是任意顺序,其实就是求启动资金的最大值。总的思路是这样的:

  • 先赔后赚,将交易划分成两部分,一部分赔钱一部分赚钱
  • 对于赔钱的交易,优先按回款从小到大排序,这样为了完成所有交易所需的钱最多
  • 对于赚钱的交易,按支出顺序从大到小排序,只要能完成最大支出的交易,那么后续交易也一定能完成

代码


/**
 * @date 2025-01-25 20:42
 */
public class MinimumMoney2412 {

    public long minimumMoney(int[][] transactions) {
        Arrays.sort(transactions, (a, b) -> {
            int diffa = a[1] - a[0];
            int diffb = b[1] - b[0];
            return diffa - diffb;
        });
        int cut = 0;
        for (int[] transaction : transactions) {
            if (transaction[0] <= transaction[1]) {
                break;
            }
            cut++;
        }
        int n = transactions.length;
        int[][] a = new int[cut][];
        int[][] b = new int[n - cut][];
        System.arraycopy(transactions, 0, a, 0, cut);
        System.arraycopy(transactions, cut, b, 0, n - cut);
        Arrays.sort(a, (x, y) -> x[1] - y[1]);
        Arrays.sort(b, (x, y) -> y[0] - x[0]);
        long res = 0;
        long remainder = 0;
        for (int[] item : a) {
            if (remainder < item[0]) {
                res += item[0] - remainder;
            }
            remainder = item[1];
        }
        if (b.length > 0 && remainder < b[0][0]) {
            res += b[0][0] - remainder;
        }
        return res;
    }

}

性能

1561.你可以获得的最大硬币数目

目标

有 3n 堆数目不一的硬币,你和你的朋友们打算按以下方式分硬币:

  • 每一轮中,你将会选出 任意 3 堆硬币(不一定连续)。
  • Alice 将会取走硬币数量最多的那一堆。
  • 你将会取走硬币数量第二多的那一堆。
  • Bob 将会取走最后一堆。
  • 重复这个过程,直到没有更多硬币。

给你一个整数数组 piles ,其中 piles[i] 是第 i 堆中硬币的数目。

返回你可以获得的最大硬币数目。

示例 1:

输入:piles = [2,4,1,2,7,8]
输出:9
解释:选出 (2, 7, 8) ,Alice 取走 8 枚硬币的那堆,你取走 7 枚硬币的那堆,Bob 取走最后一堆。
选出 (1, 2, 4) , Alice 取走 4 枚硬币的那堆,你取走 2 枚硬币的那堆,Bob 取走最后一堆。
你可以获得的最大硬币数目:7 + 2 = 9.
考虑另外一种情况,如果选出的是 (1, 2, 8) 和 (2, 4, 7) ,你就只能得到 2 + 4 = 6 枚硬币,这不是最优解。

示例 2:

输入:piles = [2,4,5]
输出:4

示例 3:

输入:piles = [9,8,7,6,5,1,2,3,4]
输出:18

说明:

  • 3 <= piles.length <= 10^5
  • piles.length % 3 == 0
  • 1 <= piles[i] <= 10^4

思路

3n 堆硬币,每次操作可以任选其中3堆,Alice 取走数量最多的那堆硬币,我们取走硬币数量第二多的,Bob 取走剩下的。求我们能获得的最大硬币数目。

为了使我们获取的硬币数量最多,Bob 每次只能取数量最少的那堆,将数量多的留下。根据这种贪心策略,我们可以将每堆硬币按个数从小到大排序,从倒数第二个开始每隔两个向前取,同时 Bob 从第一个往后挨个取,直到这两个指针相遇。

代码


/**
 * @date 2025-01-22 8:50
 */
public class MaxCoins1561 {

    public int maxCoins(int[] piles) {
        Arrays.sort(piles);
        int res = 0;
        int n = piles.length;
        int k = 0;
        for (int i = n - 2; i > k; i -= 2, k++) {
            res += piles[i];
        }
        return res;
    }
}

性能

3219.切蛋糕的最小总开销II

目标

有一个 m x n 大小的矩形蛋糕,需要切成 1 x 1 的小块。

给你整数 m ,n 和两个数组:

  • horizontalCut 的大小为 m - 1 ,其中 horizontalCut[i] 表示沿着水平线 i 切蛋糕的开销。
  • verticalCut 的大小为 n - 1 ,其中 verticalCut[j] 表示沿着垂直线 j 切蛋糕的开销。

一次操作中,你可以选择任意不是 1 x 1 大小的矩形蛋糕并执行以下操作之一:

  1. 沿着水平线 i 切开蛋糕,开销为 horizontalCut[i] 。
  2. 沿着垂直线 j 切开蛋糕,开销为 verticalCut[j] 。

每次操作后,这块蛋糕都被切成两个独立的小蛋糕。

每次操作的开销都为最开始对应切割线的开销,并且不会改变。

请你返回将蛋糕全部切成 1 x 1 的蛋糕块的 最小 总开销。

示例 1:

输入:m = 3, n = 2, horizontalCut = [1,3], verticalCut = [5]
输出:13
解释:
沿着垂直线 0 切开蛋糕,开销为 5 。
沿着水平线 0 切开 3 x 1 的蛋糕块,开销为 1 。
沿着水平线 0 切开 3 x 1 的蛋糕块,开销为 1 。
沿着水平线 1 切开 2 x 1 的蛋糕块,开销为 3 。
沿着水平线 1 切开 2 x 1 的蛋糕块,开销为 3 。
总开销为 5 + 1 + 1 + 3 + 3 = 13 。

示例 2:

输入:m = 2, n = 2, horizontalCut = [7], verticalCut = [4]
输出:15
解释:
沿着水平线 0 切开蛋糕,开销为 7 。
沿着垂直线 0 切开 1 x 2 的蛋糕块,开销为 4 。
沿着垂直线 0 切开 1 x 2 的蛋糕块,开销为 4 。
总开销为 7 + 4 + 4 = 15 。

说明:

  • 1 <= m, n <= 10^5
  • horizontalCut.length == m - 1
  • verticalCut.length == n - 1
  • 1 <= horizontalCut[i], verticalCut[i] <= 10^3

思路

有一块 m x n 的蛋糕,horizontalCut[i] 表示水平切第 i 行的开销,verticalCut[i] 表示垂直切第 i 列的开销。求将蛋糕切成 1 x 1 小块的最小代价。

切蛋糕的最小总开销I 相比数据范围扩大了,返回值是 long 型。

代码


/**
 * @date 2024-12-26 16:38
 */
public class MinimumCost3219 {

    public long minimumCost(int m, int n, int[] horizontalCut, int[] verticalCut) {
        int horizontalPart = m;
        int verticalPart = n;
        Arrays.sort(horizontalCut);
        Arrays.sort(verticalCut);
        int h = 0;
        int v = 0;
        long res = 0;
        while (h < m - 1 || v < n - 1) {
            int hcost = h >= m - 1 ? Integer.MAX_VALUE : horizontalCut[h];
            int vcost = v >= n - 1 ? Integer.MAX_VALUE : verticalCut[v];
            if (hcost < vcost) {
                res += verticalPart * hcost;
                horizontalPart--;
                h++;
            } else {
                res += horizontalPart * vcost;
                verticalPart--;
                v++;
            }
        }
        return res;
    }

}

性能