CF 500E. Hills DP(状态设置)

题意:长度为n的序列a,定义位置p为合法,当 a[p]>a[p-1] && a[p]>a[p+1].

操作:选择某个pos,令a[pos]--.

n<=5e3,a[i]<=1e5. 对k=[1...n/2] 询问,至少k个位置合法时的最小操作次数?

 

先分析一下: 位置p合法 则位置 p-1, p+1都为非法.  

若a[p-1]>p 或者 a[p+1] > a[p]  为了让a[p]合法,最少要把a[p-1],a[p+1]变为a[p]-1.

 

求最优解,无法贪心时,开始考虑DP...

 

设dp[i][k] 为前n个数中,总共有k个合法位置,并且位置i为最后一个合法位置时的最小操作次数.

dp[i][k] = min(  min(dp[1,2...i-3][k-1])+fun(a[i],a[i-1]  ,  dp[i-2][k-1] +fun(min(a[i-1],a[i-2]-1),a[i]) )   + fun(a[i+1[,a[i])  .

res[k]= min(res[k+1],dp[1:n][k]).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+5,M=2600,inf=0x3f3f3f3f;
int n,a[N],dp[N][M],res[N];
int fun(int x,int y){
	// to make x > y;
	int op=y-x+1;
	if(op<0)	op=0;
	return op;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)	cin>>a[i];
	memset(dp,inf,sizeof(dp));
	memset(res,inf,sizeof(res));
	int m=(n+1)/2;
	for(int i=1;i<=n;i++)	dp[i][1]=fun(a[i],a[i-1])+fun(a[i],a[i+1]),res[1]=min(res[1],dp[i][1]); 
	for(int k=2;k<=m;k++){
		int mn=inf;
		for(int i=2;i<=n;i++){
			if(i-3>=1)	mn=min(mn,dp[i-3][k-1]);
		
			int v1=mn+fun(a[i],a[i-1])+fun(a[i],a[i+1]);
			int v2=dp[i-2][k-1]+fun(a[i],min(a[i-1],a[i-2]-1))+fun(a[i],a[i+1]);
			dp[i][k]=min(v1,v2);
		
			res[k]=min(res[k],dp[i][k]);
		//	cout<<i<<' '<<k<<' '<<v1<<' '<<v2<<' '<<dp[i][k]<<'\n';
		}
	}
	for(int k=m-1;k>=1;k--)	res[k]=min(res[k],res[k+1]);
	for(int i=1;i<=m;i++)	cout<<res[i]<<' ';
	return 0;
}

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值