CF1012C 题解

题目传送门

一道蛮有意思的 \(\text{dp}\) 好题。

题目分析

不会吧,不会真的有人看到这道题没有想到贪心吧...

只能说考场如果死磕贪心解法会死得很惨,想了几个搞法正确性明显没保证。

在贪心被否决后,动规做法就是显然的。

设计状态 \(dp_{i,j}\) 表示前 \(i\) 个数中,选择了 \(j\) 个数,然后发现对于第 \(i\) 个数我们还要判断选或不选。因此额外设计一维,即 \(dp_{i,j,k}\) 三维。

不难发现如果我们要选择一个数 \(a_i\),则与它相邻的 \(a_{i-1}\) 一定不可以选。

  • 选择第 \(i\) 个数

此时我们不可以选择第 \(i-1\) 个数,于是转移到选择第 \(i-2\) 个数的情况。

如果选择,那么看 \(a_{i-1}\) 是否比 \(a_{i-2}\) 高,如果是要减少 \(a_{i-1}\)\(a_{i-2}-1\) ,然后再看它是否比 \(a_i\) 高,是的话还要再减。

如果不选,那么直接看 \(a_{i-1}\) 要不要减就可以了。

  • 不选择第 \(i\) 个数

此时直接转移到 \(a_{i-1}\) 的情况,考虑 \(a_{i-1}\) 选不选即可。

贴上代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=5002;
int n,a[maxn];
int dp[maxn][maxn][2];
inline void init(){
	memset(dp,0x7f,sizeof(dp));
	dp[0][0][0]=dp[1][1][1]=dp[1][0][0]=0; 
    cin>>n;
    for(register int i=1;i<=n;++i) cin>>a[i];
} 
int main(){
	init();
    for(register int i=2;i<=n;++i){
        dp[i][0][0]=dp[i-1][0][0];
        for(register int j=1;j<=((i+1)>>1);++j){
            dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]+max(a[i]-a[i-1]+1,0));
            dp[i][j][1]=min(dp[i-2][j-1][0]+max(a[i-1]-a[i]+1,0),dp[i-2][j-1][1]+max(a[i-1]-min(a[i-2],a[i])+1,0));
        }
    }
    for(register int i=1;i<=((n+1)>>1);++i) cout<<min(dp[n][i][0],dp[n][i][1])<<" ";
}
posted @ 2022-10-10 17:42  yizhixiaoyun  阅读(36)  评论(0)    收藏  举报