Hard
题目描述
有一块 m x n 的蛋糕需要切成 1 x 1 的小块。
给你整数 m、n 和两个数组:
horizontalCut大小为 m - 1,其中horizontalCut[i]表示沿水平线 i 切割的费用。verticalCut大小为 n - 1,其中verticalCut[j]表示沿垂直线 j 切割的费用。
在一次操作中,你可以选择任意一块尚未成为 1 x 1 的蛋糕,并执行以下切割之一:
- 沿水平线 i 切割,费用为
horizontalCut[i]。 - 沿垂直线 j 切割,费用为
verticalCut[j]。
切割后,这块蛋糕被分成两个不同的部分。
切割的费用只取决于该线的初始费用,不会改变。
返回将整个蛋糕切成 1 x 1 小块的最小总费用。
示例 1:
输入:m = 3, n = 2, horizontalCut = [1,3], verticalCut = [5]
输出:13
示例 2:
输入:m = 2, n = 2, horizontalCut = [7], verticalCut = [4]
输出:15
提示:
1 <= m, n <= 10^5horizontalCut.length == m - 1verticalCut.length == n - 11 <= horizontalCut[i], verticalCut[i] <= 10^3
解题思路
解题思路
这道题的核心思路是贪心算法。关键观察是:当我们进行一次切割后,这条切割线会被后续的垂直切割重复使用。
具体分析:
- 当我们先进行一次水平切割时,蛋糕被分成两行。之后每次垂直切割都会在这两行上同时进行,相当于每次垂直切割的费用要乘以当前的行数。
- 同样,当我们先进行垂直切割时,每次后续的水平切割费用要乘以当前的列数。
贪心策略:
- 总是优先选择费用最高的切割线进行切割
- 使用两个指针分别指向已排序的水平和垂直切割数组
- 维护当前的行数和列数,计算每次切割的实际费用
算法步骤:
- 将水平和垂直切割费用分别按降序排序
- 使用双指针技术,每次选择费用更高的切割方向
- 记录当前已有的行数和列数,计算总费用
这种贪心策略是最优的,因为费用高的切割线应该尽早执行,这样可以最小化后续切割的重复费用。
时间复杂度主要来自排序操作。
代码实现
class Solution {
public:
long long minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
sort(horizontalCut.rbegin(), horizontalCut.rend());
sort(verticalCut.rbegin(), verticalCut.rend());
long long cost = 0;
int h = 0, v = 0;
int rows = 1, cols = 1;
while (h < horizontalCut.size() && v < verticalCut.size()) {
if (horizontalCut[h] > verticalCut[v]) {
cost += (long long)horizontalCut[h] * cols;
rows++;
h++;
} else {
cost += (long long)verticalCut[v] * rows;
cols++;
v++;
}
}
while (h < horizontalCut.size()) {
cost += (long long)horizontalCut[h] * cols;
h++;
}
while (v < verticalCut.size()) {
cost += (long long)verticalCut[v] * rows;
v++;
}
return cost;
}
};
class Solution:
def minimumCost(self, m: int, n: int, horizontalCut: List[int], verticalCut: List[int]) -> int:
horizontalCut.sort(reverse=True)
verticalCut.sort(reverse=True)
cost = 0
h = v = 0
rows = cols = 1
while h < len(horizontalCut) and v < len(verticalCut):
if horizontalCut[h] > verticalCut[v]:
cost += horizontalCut[h] * cols
rows += 1
h += 1
else:
cost += verticalCut[v] * rows
cols += 1
v += 1
while h < len(horizontalCut):
cost += horizontalCut[h] * cols
h += 1
while v < len(verticalCut):
cost += verticalCut[v] * rows
v += 1
return cost
public class Solution {
public long MinimumCost(int m, int n, int[] horizontalCut, int[] verticalCut) {
Array.Sort(horizontalCut, (a, b) => b.CompareTo(a));
Array.Sort(verticalCut, (a, b) => b.CompareTo(a));
long cost = 0;
int h = 0, v = 0;
int rows = 1, cols = 1;
while (h < horizontalCut.Length && v < verticalCut.Length) {
if (horizontalCut[h] > verticalCut[v]) {
cost += (long)horizontalCut[h] * cols;
rows++;
h++;
} else {
cost += (long)verticalCut[v] * rows;
cols++;
v++;
}
}
while (h < horizontalCut.Length) {
cost += (long)horizontalCut[h] * cols;
h++;
}
while (v < verticalCut.Length) {
cost += (long)verticalCut[v] * rows;
v++;
}
return cost;
}
}
var minimumCost = function(m, n, horizontalCut, verticalCut) {
horizontalCut.sort((a, b) => b - a);
verticalCut.sort((a, b) => b - a);
let cost = 0;
let h = 0, v = 0;
let rows = 1, cols = 1;
while (h < horizontalCut.length && v < verticalCut.length) {
if (horizontalCut[h] > verticalCut[v]) {
cost += horizontalCut[h] * cols;
rows++;
h++;
} else {
cost += verticalCut[v] * rows;
cols++;
v++;
}
}
while (h < horizontalCut.length) {
cost += horizontalCut[h] * cols;
h++;
}
while (v < verticalCut.length) {
cost += verticalCut[v] * rows;
v++;
}
return cost;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(m log m + n log n) | 主要来自对两个数组的排序操作 |
| 空间复杂度 | O(1) | 只使用了常数额外空间,不考虑排序的空间开销 |
相关题目
- . Minimum Cost for Cutting Cake I (Medium)