Jzoj5408 Dark

来源:互联网 发布:淘宝ab单试用平台 编辑:程序博客网 时间:2024/05/17 07:07
LichKing 希望收集邪恶的黑暗力量,并依靠它称霸世界。
世间的黑暗力量被描述成一个长度为N 的非负整数序列{Ai},每次它可以选择这个序列中的两个相邻的正整数,让他们的值同时减一并获得一点邪恶力量,直到不存在满足条件的数。

然而你不希望他能够得逞,所以你会使得他收集的能量尽可能少。

这个题目存在线性的dp,这里先说一种ΣAi的方法

我们设f[i][j][0/1]表示现在正在做第i位,这一位为j,前一位是否为0的最优解

显然我们不能在状态中保留连续的两个非零数

发现可以如下转移

f[i][j][0]=min({f[i-1][a[i]-j-k][1]+a[i]-j},f[i-1][a[i]-j][0]+a[i]-j)

f[i][j][1]=f[a[i]-j][0]+a[i]-j

这样在加上滚动数组即可

关于线性方法,我们设f[i]表示处理道第i位,且必须将第i位清零的答案

那么显然,不管第i位前后是否为0,我们都可以花Ai的代价将其清除(因为这样显然不是最优情况)

那么我们考虑可以从f[i-2]中转移过来,代价是ai

因为我们显然可以只讲ai清空而不管ai-1(从f[i-2]转移),也可以考虑清空ai和ai-1(从f[i-1])

我们还可以考虑从f[i-3]转移过来

这样必须清空a[i]和a[i-1]而不管a[i-2]

这样,最后的答案就是f[n+1]

方法1:code

#pragma GCC optimize("O3")#pragma G++ optimize("O3")#include<stdio.h>#include<string.h>#include<algorithm>#define N 5000010using namespace std;int f[N][2],g[N][2],s[N],a[N],n,A=1<<30;inline void gmin(int& x,int y){ x>y?x=y:0; }int main(){freopen("dark.in","r",stdin);freopen("dark.out","w",stdout);scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",a+i);memset(f,0x3f,sizeof f); memset(g,0x3f,sizeof f); f[0][0]=s[0]=0;for(int i=1;i<=n;++i){for(int j=a[i-1]+1;j<=a[i];++j)f[j][0]=f[j][1]=s[j]=0x3f3f3f3f;for(int j=a[i];~j;--j) g[j][0]=g[j][1]=0x3f3f3f3f;for(int j=a[i];~j;--j){gmin(g[j][0],s[a[i]-j]+a[i]-j);gmin(g[j][0],f[a[i]-j][0]+a[i]-j);gmin(g[j][1],f[a[i]-j][0]+a[i]-j);}s[a[i]+1]=0x3f3f3f3f;for(int j=a[i];~j;--j){f[j][0]=g[j][0];f[j][1]=g[j][1];s[j]=min(s[j+1],f[j][1]);}}A=f[0][0];for(int i=0;i<=a[n];++i) gmin(A,f[i][1]);printf("%d\n",A);}

线性dp代码:

#include<bits/stdc++.h>using namespace std;#define N 100010int n,a[N],f[N];int input() {int f=1,g=0; char c=getchar();while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9') {g=(g<<3)+(g<<1)+c-'0';c=getchar();}return f*g;}int main() {freopen("dark.in","r",stdin);freopen("dark.out","w",stdout);n=input()+3;for(int i=3;i<n;i++) a[i]=input();for(int i=3;i<=n;i++)f[i]=min(f[i-2]+a[i],f[i-3]+max(a[i-1],a[i]));printf("%d\n",f[n]);return 0;}

原创粉丝点击