题目:有一个 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
的蛋糕块的 最小 总开销。
人话:参考0X3F题解,问题可以被理解成,最小生成树的 Kruskal 算法 的使用,也就是:
然后需要注意的是,如果当下要把竖向 的切面连起来,就要看这时候横向的有多少组,有几组就要连几次,最开始的组数当然就是 m-1 和 n-1了。代码里使用 i j 来表示已经粘了的次数。
小知识 :&& 的优先级比 || 要高
易错:纵向连完了, 或者 横向存在更小的边时,做一次横向连接,因此是两个进入条件:
如果把两种条件写在一起,就会出现先后顺序的问题,写错了往往会造成越界,具体如下:
// 正确写法
if( j == n - 1 || i < m - 1 && horizontalCut[i] < verticalCut[j]){}
// 越界写法
if( i < m - 1 && horizontalCut[i] < verticalCut[j] || j == n - 1){}
把边界判定条件提前,是更明智的选择。
class Solution {
public:
long long minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
int i = 0, j = 0; // i j 表示当前要去合并的切线
long long ret = 0;
std::sort(horizontalCut.begin(), horizontalCut.end()); // 最小生成树排序
std::sort(verticalCut.begin(), verticalCut.end());
while(i < m - 1 || j < n - 1){
// j == n - 1放前面,否则会越界报错
if( i < m - 1 && horizontalCut[i] < verticalCut[j] || j == n - 1){
ret += (n - j) * horizontalCut[i];
i++;
}else{
ret += (m - i) * verticalCut[j];
j++;
}
}
}
};