bzoj1704/poj3276[Usaco2007 Mar]Face The Right Way自动转身机
题目大意:
有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; }