bzoj1704/poj3276[Usaco2007 Mar]Face The Right Way自动转身机

题目链接:bzojpoj

题目大意:

有N(1≤N≤5000)只牛站成一排,有些朝前站着.有些朝后站着.农夫约翰需要让所有的牛都朝前站着.幸运的是约翰最近买了一个自动转身机.这个神奇的机器能使K(1≤K≤N)只连续的牛转身. 因为约翰从来都不改变K的价值,请帮助他求出K,使旋转次数M达到最小.同时要求出对应的M.如果不够K只牛的话,转身机是不能工作的。


题解:

额写的穷举题

一开始看这道题:怎么可能才两星啊???啊好难啊随便YY了想一下做法肯定超时啊。然后想想想!打了个感觉很玄学的剪枝还是TLE,继续想优化,终于!我我我我想到了一个O(n^2)的做法了!真高兴OvO。。好吧虽然左←边的神犇们应该都能一眼秒了qwq。

额说正解(为什么最近碎碎念多了那么多呢

首先先把牛什么的弄成01串,0是朝后的,1是朝前的,目标就是把串变成全1串。翻转一次就是那一段数都异或1。

然后枚举K值。对于一个K值,我们再把串从左到右扫一遍求M,为什么可以扫一遍就求到M了呢。

因为我们要知道,扫到某一位的时候如果之前的翻转操作并不能让现在这一位变成1的话,那就一定要以这一位开始使用一次机器,只有这样才能使这位的零变成1(因为后面的操作都不能影响这一位了啊)。【哦,可能要说下对于以某一头牛开始的翻转操作最多翻一次,翻两次的话就抵消效果了。

所以每次维护一个 翻转操作对串的影响 的数组就好了,↑这样求到的M一定是对枚举的K来说最小的(没有多余的操作啊)。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 5010

int sum[maxn],a[maxn];//sum[]就是维护的那个数组
int main()
{
	//freopen("cowturn.in","r",stdin);
	//freopen("cowturn.out","w",stdout);
	int n,i,j,k,cnt;char c;
	scanf("%d",&n);
	for (i=1;i<=n;i++)
	{
		scanf("\n%c",&c);
		if (c=='B') a[i]=0;
		else a[i]=1;
	}
	for (i=n;i>=1;i--)//K要越大越好所以从后往前枚举
	{
		cnt=0;bool bo=true;
		for (j=1;j<=n;j++) sum[j]=0;
		for (j=1;j<=n;j++)
		 if (a[j]^sum[j-1]^sum[j]) sum[j]^=sum[j-1];//之前操作的影响向后传递
		 else {
			 if (j+i-1<=n) cnt++;//如果还能翻就累加次数
		 	 else {bo=false;break;}
			 sum[j]=sum[j]^sum[j-1]^1;sum[j+i]^=1;//原理同+1-1什么的啊
			 //就是把[j,j+i-1]这段区间都异或1,到j+i的时候再异或一下就抵消了
		}
		if (bo) break;
	}
	printf("%d %d\n",i,cnt);
	return 0;
}


posted @ 2016-11-05 15:26  OxQ  阅读(186)  评论(0编辑  收藏  举报