COGS 498 TYVJ 1094 矩形分割 贪心

这道题最容易想到的是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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值