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;
}