Codeforces Round #500 (Div. 1) [based on EJOI] C. Hills

题意

  有一座城市,城市中有 n(1n5000) n ( 1 ≤ n ≤ 5000 ) 座山,是从左到右按一行排列的,每座山有一个已知的高度 h h ,一座房子能在第i座山上建造,当且仅当 hi1<hi h i − 1 < h i hi>hi+1 h i > h i + 1 ,现在这座城市的市长想对这些山进行修整,他们可以花一天的时间将某一座山的高度减掉 1 1 。现在,对于所有的k(1kn2),他们想知道最少需要多少天可以建造出 k k 幢房子(除了调整山的高度外,其他时间一律不计)。

分析

  首先我们可以考虑DP,用f[i][j][k]表示现在DP到第 i i 座山,1 i座山里有 j j 幢可以建房子,状态为k(0k2)的最小天数。当 k=0 k = 0 时表示第 i i 座山和第i1座山都不用来建房子;当 k=1 k = 1 时表示第 i i 座山不建房子,第i1座山建房子;当 k=2 k = 2 时表示第 i i 座山建房子,第i1座山建房子,然后就可以进行转移了。不过要注意,这里我们将第 i i 座山设为建房子的那座山时,对第i+1座山的影响放在第 i+1 i + 1 位进行计算,这样防止有重复计算等麻烦出现。在表示完状态后就可以轻易地想出DP转移的式子了。
   f[i][j][0]=min(f[i1][j][0],f[i1][j][1]) f [ i ] [ j ] [ 0 ] = m i n ( f [ i − 1 ] [ j ] [ 0 ] , f [ i − 1 ] [ j ] [ 1 ] ) 如果这一座山和前一座山都不是山峰的转移。
   f[i][j][1]=f[i1][j][2]+max(0,h[i]h[i1]+1) f [ i ] [ j ] [ 1 ] = f [ i − 1 ] [ j ] [ 2 ] + m a x ( 0 , h [ i ] − h [ i − 1 ] + 1 ) 这里是计算第 i1 i − 1 座建房子是对第 i i 座山的影响。
  f[i][j][2]=min(f[i1][j1][0]+max(0,h[i1]h[i]+1),f[i1][j1][1]+max(0,min(h[i2]1,h[i1])h[i]+1))
  这里是分两种情况考虑,当第 i2 i − 2 座山也被拿来建房子时,要考虑第 i2 i − 2 座山对第 i1 i − 1 座山的影响与第 i i 座山拿来建房子是对第i1座山的影响。如果第 i2 i − 2 座山不拿来建房子,那就不用考虑了。
  最后对于所有的 k k ,只要在f[n][k][0],f[n][k][1],f[n][k][2]里取 min m i n 就好了。

Code

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=5005;
int n,h[maxn],f[maxn][maxn][3];
int main() {
    memset(f,0x3f,sizeof f);
    read(n);
    for(int i=1;i<=n;++i)
        read(h[i]);
    f[0][0][0]=0;
    for(int i=1;i<=n;++i) {
        for(int j=0;j<=i;++j) {
            f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1]);
            f[i][j][1]=f[i-1][j][2]+max(0,h[i]-h[i-1]+1);
            if(j)
                f[i][j][2]=min(f[i-1][j-1][0]+max(0,h[i-1]-h[i]+1),f[i-1][j-1][1]+max(0,min(h[i-2]-1,h[i-1])-h[i]+1));
        }
    }
    for(int i=1;i<=(n+1)/2;++i)
        printf("%d ",min(f[n][i][2],min(f[n][i][0],f[n][i][1])));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值