题面
LichKing 希望收集邪恶的黑暗力量,并依靠它称霸世界。
世间的黑暗力量被描述成一个长度为N 的非负整数序列{Ai},每次它可以选择这个序列中的两个相邻的正整数,让他们的值同时减一并获得一点邪恶力量,直到不存在满足条件的数。
然而你不希望他能够得逞,所以你会使得他收集的能量尽可能少。
分析
考虑从左到右搞,发现你只需要知道上一个剩余多少与上上个是否还有,就可以选择去除多少个,上一个是否必须清完。 比赛时没想到是因为不知道如何设状态。想了设f[i][j]的,但是不知怎地认为转移需要二维…
发现当前状态可以划分为f[i][j][0/1]表示当前剩余j个,上个是否用完,这样1..i搞完的最小代价(没有相邻的1,除了最后两个位置)。
f[i][j][0]=f[i−1][a[i]−j][0/1]+a[i]−j
f[i][j][1]=f[i−1][j′][0]+a[i]−j, j′>a[i]−j
答案就是f[n][x][0]或f[n][0][1]
加上后缀min优化,时间O(sigma ai)
Demo
#include <cstdio>
#include <iostream>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N = 1e5+10;
int n,ans;
int a[N],f[2][5000010][2],g[2][5000010],o;
int main() {
freopen("dark.in","r",stdin);
//freopen("dark.out","w",stdout);
cin>>n; for (int i=1; i<=n; i++) scanf("%d",&a[i]);
memset(f,127,sizeof f);
f[o][0][0]=0;
for (int i=1; i<=n; i++) {
o=1-o;
for (int j=0; j<=a[i]; j++) {
if (a[i]-j<=a[i-1])
f[o][j][0]=min(f[1-o][a[i]-j][1],f[1-o][a[i]-j][0])+a[i]-j;
else f[o][j][0]=1<<30;
if (a[i]-j+1<=a[i-1])
f[o][j][1]=g[1-o][a[i]-j+1]+a[i]-j;
else f[o][j][1]=1<<30;
}
g[o][a[i]+1]=1<<30;
for (int j=a[i]; j>=0; j--)
g[o][j]=min(f[o][j][0],g[o][j+1]);
}
cout<<min(f[o][0][1],g[o][0])<<endl;
}