题意:
给定一个长度为(n)的({-1, 0, 1})组成的序列,你可以进行(x_i=x_i+x_{i-1})这样的操作,求最少操作次数使其变成不降序列。((n le 1000000))
分析:
我们考虑第(i)个数,如果(x_i < x_{i-1}),要想(x_i ge x_{i-1}),那么(x_i)至少要加一次(x_{i-1})才能大过(x_{i-1})(当然(x_{i-1} < 0)那么永远不可能了)。
题解
然后我们猜测,最终的最优序列也一定由(\{-1, 0, 1\})三个数组成。我们来证明一下:
假设第一个不是(\{-1, 0, 1\})数的位置为(p),且假设(p < n),则容易知道(x_p > 1)。当(x_{p+1} = -1)时,我们要加2次才能大于等于(x_p),当(x_{p+1}=0或1)时,我们要加1次。而由于(x_p > 1),那么说明(x_p)也一定能够等于(1),这是因为(x_{p-1})必然等于(1)(否则(x_p)就不会大于(1))。而当(x_p=1)时,对于(x_{p+1} in \{-1, 0, 1\})我们分别只需要加2次、加1次和加0次就能满足(x_{p+1} ge x_p)。显然比(x_p > 1)要优。得证。
于是我们设(d[i, j])表示前(i)个元素当前元素为(j)时的最少操作次数,然后推一下就行了..
#include <bits/stdc++.h>
using namespace std;
const int oo=~0u>>1;
int n, d[2][3];
int main() {
scanf("%d", &n);
int x; scanf("%d", &x);
int *now=d[0], *last=d[1];
last[0]=last[1]=last[2]=oo;
if(x==-1) last[0]=0;
if(x==0) last[1]=0;
if(x==1) last[2]=0;
for(int i=2; i<=n; ++i) {
scanf("%d", &x);
now[0]=now[1]=now[2]=oo;
if(last[0]!=oo) {
now[0]=last[0]+x+1;
if(x>=0) now[1]=last[0]+x;
if(x==1) now[2]=last[0];
}
if(last[1]!=oo) {
if(x==0) now[1]=min(now[1], last[1]);
if(x==1) now[2]=min(now[2], last[1]);
}
if(last[2]!=oo) {
now[2]=min(now[2], last[2]+1-x);
}
swap(now, last);
}
swap(now, last);
int ans=min(min(now[0], now[1]), now[2]);
if(ans==oo) puts("BRAK");
else printf("%d
", ans);
return 0;
}