#bzoj2240#积木游戏(DP? 贪心?)

27 篇文章 0 订阅
15 篇文章 0 订阅

2240: 积木游戏

时间限制: 1 Sec  内存限制: 128 MB

题目描述

小时候我们都喜欢玩积木。这里的积木都是单位边长的正方体块,多个积木可以堆成一个“高木”,“高木”的高度就是叠放的积木块个数。多个“高木”形成一个排列,如果高度满足先严格上升再严格下降,则称这个排列为一座山峰。严格的定义是:假设有N个高木从左到右排列,第i个高度为H[i](i=1,2,……N)。那么如果存在一个整数k[2,N-1],使得对所有的位置i,下式都成立,则称H是一座山峰。

H[i]>H[i-1],1<i<=k

H[i]>H[i+1],k<i<=N

现在你有一个超级工具,每次操作可以给一段连续的区间各位置都叠放上一块积木,使得高度同时增加1个单位,现在有一个“高木”排列,需要将其改造为一座山峰,只允许使用这种超级工具,最少需要操作几次可以达到这个目标呢?假设积木无限供应。

输入

输入文件只有一组数据。

第一行包含一个整数N,为上文提到的初始排列中“高木”的个数。

第二行包含N个正整数,表示由左到右的N个位置“高木”的初始高度H[i],数字由空格隔开。

输出

输出包含一个整数,表示所需要的最少的操作次数。

样例输入

6
3 4 3 6 7 8

样例输出

2

提示

对于30%的数据,满足N<=20,H[i]<=50.


对于50%的数据,满足N<=100,H[i]<=1000


对于全部的数据,满足3<=N<=105,H[i]<=107


先考虑一个更简单的问题,如果我们的目标排列是严格递增,问需要多少次操作?

考虑到操作的这样几个性质:

第一:操作是无序的。

第二:操作的区间可以由i~j扩充到i~N,而并不影响最优解。

第一点显而易见,第二点简单证明如下:

对于第i根高木,如果把所有的操作的右边界都扩充到N,则i会额外增加许多高度,可以想象的是排在它右边的高柱j,增加的高度会更多。如果他们原来的操作可以形成一个严格上升的序列,则现在一定还是一个严格上升的序列。所以,不影响最优解。

于是,我们可以使用贪心的方法了。

对于第i根高木,如果其高度h[i]<=h[i-1],则需要对i~N进行h[i-1]-h[i]+1次操作;否则就不需要。

最优解为每根高木所需要的操作次数之和,用ans表示。

现在回到原问题。

原问题可以看做是两个严格上升序列的组合。

所以,最终最优解为两个上升序列所需操作的最大值,即ans=max(ans1,ans2)


注意数据范围超int


Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;

typedef long long LL;

const int Max = 100000;

int N;
int H[Max + 5];
LL Dp[Max + 5][2];

void getint(int & num){
    char c; int flg = 1;    num = 0;
    while((c = getchar()) < '0' || c > '9')  if(c == '-')    flg = -1;
    while(c >= '0' && c <= '9'){    num = num * 10 + c - 48;    c = getchar();}
    num *= flg;
}

int main(){
    //freopen("game.in", "r", stdin);
    //freopen("game.out", "w", stdout);
    getint(N);
    for(int i = 1; i <= N; ++ i)    getint(H[i]);
    for(int i = 2; i <= N; ++ i)
        Dp[i][0] = Dp[i - 1][0] + max(0, H[i - 1] - H[i] + 1);
    for(int i = N - 1; i >= 1; -- i)
        Dp[i][1] = Dp[i + 1][1] + max(0, H[i + 1] - H[i] + 1);
    LL Ans = 0x7f7f7f7f7f7fll;
    for(int i = 1; i <= N; ++ i)
        Ans = min(Ans, max(Dp[i][0], Dp[i][1]));
    printf("%lld\n", Ans);
    return 0;
}








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值