P2882 Face The Right Way - USACO07MAR

这道题没有一个比较详细的题解,我来提供一份。
首先我们可以知道,反转区间的顺序对结果没有影响,而且一个区间如果翻转两次以上是没有意义的,所以,问题就变成了求哪些区间需要反转。
我们枚举k。对于每一个k,我们设计一个calc函数来判断k的操作次数。
显然的,我们可以设计出一种方法,就是每一次都检查最左端,然后进行反转,很容易写出下面的calc函数。

int calc(int k) {
	int ans = 0;
	int i; 
	for(i = 1; i + k - 1 <= N; i++) {
		if(f[i] == 1) {
			for(int j = i; j <= i + k - 1; j++) {
				f[j] = !f[j];
			}
			ans++;
		}
	}
	for(i--; i <= N; i++) {
		if(f[i] == 1) return -1;
	}
	return ans;
}

这样的检查方式复杂度为O(n2),再结合枚举k,总的复杂度是O(n3),这样的复杂度可以通过70%的数据,但还不够好。
我们来考虑怎么优化。显然的,我们没有必要去记录每一个的状态,我们只需要存储每一个区间是否反转过。所以,我们定义
f[i]为区间[i, i+k-1]是否反转。
这样,反转的复杂度就降到了O(1),总的复杂度就降到了O(n2),这样我们就可以AC这道题了。
对于实现上还有一个问题,就是怎么判断每一个格子的状态,这个问题我们留给读者思考。
下面贴上calc的代码。

int calc(int K) {
	memset(f, 0, sizeof(f));
	int ans = 0;
	int sum = 0;
	for(int i = 0; i + K <= N; i++) {
		if((g[i] + sum) % 2 != 0) {
			ans++;
			f[i] = 1;
		}
		sum += f[i];
		if(i - K + 1 >= 0) sum-=f[i-K+1];
	}
	for(int i = N - K + 1; i < N; i++) {
		if((g[i] + sum) % 2 != 0) {
			return -1;
		}
		if(i-K+1 >= 0) {
			sum-=f[i-K+1];
		}
	}
	return ans;
}

如果有问题,可以私信。

posted on 2016-11-07 10:51  蒟蒻konjac  阅读(262)  评论(0编辑  收藏  举报