这道题最容易想到的是DP,但是基于数据范围,DP并不是很容易实现。空间会爆。
于是可以想贪心,我们每次都割开最大代价的边。如何证明这个的正确性呢,其实我一开始不是这么想的贪心,我想的是:割一条竖边对其他竖边是没有影响的,而会使横边代价都多计算一次。那么我们对于一块木板,比较横边代价和以及竖边代价和,在那个比较大的一类边中选一个代价最大的。
看似比较合理,但是实际操作一下却能证明它是错的,那么我们回到最初的那个正确的贪心思路:每次都选代价最大的边。对于那个错误的思路,假如有两次割取,一次割去i,其后一次割去j,j < i。既然i在j前面割去,那么就是i的那一类边的和大于j那一类,但是这么考虑,我们仅仅交换i和j的顺序,其他的不变,代价总和是不是会变小?答案是显然会变小的。因为割i,j之前和之后的状态不变,代价不变,仅仅改变了i,j的顺序,而这个改变使花费减小。
用这种比较方式来证明贪心的正确性算是很基础的思路,主要是在和顺序,序列有关的贪心中。 新技能get√
/如果还不能接受我们还可以这么想,设s数组为最优的割取的顺序,s[i]表示第i次割的边的代价,我们考虑第i次割的边喝第i-1次不同时,前面有k次和第i次割的边不同。由于满足最优,所以当交换i与i-1必然代价不小于当前,于是有: s[i]*k + s[i-1]*(i-1-k) <= s[i-1]*(i-k) + s[i]*(k+1)。变形得s[i] >= s[i-1]。/ 貌似有问题,待证实。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n, m, ans;
int h[2001], s[2001], sumh[2001], sums[2001];
void sec(int x1, int y1, int x2, int y2){
if(x1 == x2){
ans += sums[y2-1]-sums[y1-1];
return ;
}
if(y1 == y2){
ans += sumh[x2-1]-sumh[x1-1];
return ;
}
int mxh = 0, posh, mxs = 0, poss;
for(int i = x1; i < x2; i++){
if(h[i] > mxh){
mxh = h[i];
posh = i;
}
}
for(int i = y1; i < y2; i++){
if(s[i] > mxs){
mxs = s[i];
poss = i;
}
}
ans += max(mxh, mxs);
if(mxh < mxs){
sec(x1, y1, x2, poss);
sec(x1, poss+1, x2, y2);
}else if(mxh > mxs){
sec(x1, y1, posh, y2);
sec(posh+1, y1, x2, y2);
}else{
if(sums[y2-1]-sums[y1-1] < sumh[x2-1]-sumh[x1-1]){
sec(x1, y1, posh, y2);
sec(posh+1, y1, x2, y2);
}else{
sec(x1, y1, x2, poss);
sec(x1, poss+1, x2, y2);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i < n; i++){
scanf("%d", h+i);
sumh[i] = sumh[i-1] + h[i];
}
for(int i = 1; i < m; i++){
scanf("%d", s+i);
sums[i] = sums[i-1] + s[i];
}
sec(1, 1, n, m);
printf("%d", ans);
return 0;
}