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

本文介绍了一种使用动态规划(DP)解决特定建房问题的方法。在一个由n座山组成的城市中,每座山的高度已知,目标是在规定天数内建造尽可能多的房子。房子只能在山峰上建造,且相邻山峰的高度必须满足特定条件。文章详细阐述了如何通过DP求解最少所需天数,以建造k幢房子,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

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

分析

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

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、付费专栏及课程。

余额充值