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})\)

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

Solution

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

  • 在序列中选择两个相邻的正整数 \(p_i\)\(p_{i+1}\),花费 \(x\) 的代价让它们同时减去 \(x\)\(x\in[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)(i\in [l+1,r])\),那么 \(f_{l,r}=\sum_{i=l}^{r}c_i\)

于是有:

\[\begin{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_i\gets 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\n",ans.size());
	for(int v:ans) printf("%d\n",v);
	return 0;
}

posted @ 2021-06-10 09:32  cjTQX  阅读(99)  评论(0编辑  收藏  举报