【BZOJ】3427: Poi2013 Bytecomputer

题意:

给定一个长度为\(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\n", ans);
	return 0;
}
posted @ 2015-11-22 14:36  iwtwiioi  阅读(267)  评论(0编辑  收藏  举报