C.[POI2013]BAJ-Bytecomputer
首先先说猜想:最终序列中所有数都是\(-1,0,1\),且不存在先改后面,后改前面的状态。
有了这个猜想,就可以DP了。我们设\(f_{i,j}\)表示要使位置\(i\)出现数\(j\),且前\(i\)个位置单调不降的最小费用。则我们枚举往\(a_{i+1}\)上加多少个\(j\)(明显只能加\(0/1/2\)个),判断往\(a_{i+1}\)上加上这么多\(j\)后是否仍满足单调不降,如果可以那就直接转移没问题了。
下面来讲证明。全是\(-1,0,1\)很好证,因为原本所有数都在此值域内,你要加出这个值域肯定要耗费更多代价。
不存在先改后面,后改前面的状态也很好证——首先,我们观察到执行a[i]+=a[i-1]
,肯定有一种方案使得要么它在\(a_{i-1}\)符合要求之前执行,要么它在\(a_{i-1}\)符合要求之后执行。而两次执行,都是\(+1/0/-1\),假如两次相同,那肯定可以看作一端执行两遍;有一个是\(0\),不如不执行;则只剩下一次\(+1\),一次\(-1\)的状况,但这样等价于没执行,所以也可以忽略。
则上述解法正确。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[1000100],f[1001000][3],res=0x3f3f3f3f;
int main(){
scanf("%d",&n),memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
f[1][a[1]+1]=0;
for(int i=1;i<n;i++)for(int j=-1;j<=1;j++)for(int k=0;k<=2;k++)if(a[i+1]+k*j>=j&&a[i+1]+k*j<=1)f[i+1][a[i+1]+k*j+1]=min(f[i+1][a[i+1]+k*j+1],f[i][j+1]+k);
for(int i=-1;i<=1;i++)res=min(res,f[n][i+1]);
if(res==0x3f3f3f3f)puts("BRAK");else printf("%d\n",res);
return 0;
}