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