CF933E A Preponderant Reunion【DP】

CF933E

Description

一个由 (n) 个非负整数组成的序列 ({p_i}),现在你可以执行一种操作:

  • 在序列中选择两个相邻的正整数 (p_i)(p_{i+1}),花费 (min(p_i,p_{i+1})) 的代价让它们同时减去 (min(p_i,p_{i+1}))

当序列中没有两个相邻的正整数时游戏结束,请你构造一种方案使得花费最少的代价让游戏结束。(nle 3 imes 10^5,p_ile 10^9)

Solution

原问题中操作的限制非常麻烦,考虑放宽这一限制,改为:

  • 在序列中选择两个相邻的正整数 (p_i)(p_{i+1}),花费 (x) 的代价让它们同时减去 (x)(xin[1,min(p_i,p_{i+1})])

这个新问题的答案一定 $le $ 原问题,而新问题中的最优解用在原问题中一定也满足。所以只需要求出新问题的最优解即可。

首先最终的答案一定是若干正整数,每两个相邻正整数之间都有一段 (0),因此考虑 (f[l,r]) 表示仅考虑 ([l,r])中的数,将 ([l,r]) 中的数全部清零的最小代价。设 (c_l=p_l,c_i=max(p_i-c_{i-1},0)(iin [l+1,r])),那么 (f_{l,r}=sum_{i=l}^{r}c_i)

于是有:

[egin{aligned} f_{l,r}&=f_{l,r-2}+c_{r-1}+c_r\ &=f_{l,r-2}+c_{r-1}+max(p_r-c_{r-1},0)\ &=f_{l,r-2}+max(p_r,c_{r-1})\ &le f_{l,r-2}+p_r\ &=f_{l,r-2}+f_{r,r} end{aligned} ]

这一等式告诉我们,原问题最终剩下的连续 (0) 的长度只能是 (1)(2)。可能为 (2) 是因为如果 (r=l+1),那么唯一的选择就是两个都为 (0)

接下来就很容易 (DP) 了,设 (g_i) 表示 (p_{i+1}) 最终保留下来,而 (p_i) 变为 (0) 的情况下,使前 (i+1) 个数组成的序列符合条件的最小代价,于是:

[g_{i}gets g_{i-2}+p_i\ g_igets g_{i-3}+max(p_i,p_{i-1})\ ]

构造方案就直接记录下从什么地方转移过来的就行了。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
int p[N],g[N],n;
ll f[N];
vector<int> ans;
inline void clear(int x){
	if(p[x]<=0||p[x+1]<=0) return ;
	ans.push_back(x);
	int tmp=min(p[x],p[x+1]);
	p[x]-=tmp;p[x+1]-=tmp;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&p[i]);
	memset(f,0x3f,sizeof(f));
	f[0]=0;ll x;
	for(int i=1;i<=n;++i){
		if((x=f[max(i-2,0)]+p[i])<f[i]) f[i]=x,g[i]=i-2;
		if((x=f[max(i-3,0)]+max(p[i-1],p[i]))<f[i]) f[i]=x,g[i]=i-3;
	}
	int st=f[n]<f[n-1]?n:n-1;
	for(int i=st;i>=1;i=g[i]){
		clear(i-1);
		if(g[i]==i-3) clear(i-2);clear(i);
	}
	printf("%d
",ans.size());
	for(int v:ans) printf("%d
",v);
	return 0;
}

原文地址:https://www.cnblogs.com/tqxboomzero/p/14869806.html