USACO35 翻转奶牛(尺取法)
通过这道题了解了不少有关翻转的知识呢......
首先,我们枚举翻转的区间长度k,假设i有个按钮,按下就可以让i~i+k-1翻转,那就有两个状态,按i或不按i(因为按两次相当于没按),那就往后扫一遍,假如要翻转就翻转,不用就不翻,但是这样就会有个问题,每次翻转都要把后面的k个置反,能不能优化?
可以,这就是尺取法。我们用sum表示当前区间翻转了多少次,假如第一头奶牛被翻转,而第二头奶牛也需要(1表示需要,0表示不须),而当前k=2,那就sum=1,我们用(sum+1)%2,假如等于0,就不用翻第二头奶牛了。那尺取体现在那呢,就是我们枚举的区间,前面的区间,假如不能对我当前的点造成影响了,就是i-k的位置,翻不翻转,都对当前位置没有影响,他的影响范围只有i-k~i-1,所以这时sum减去他的影响。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int n; int f[5100],c[5100]; int solve(int k) { int cnt=0,sum=0; memset(f,0,sizeof(f)); for(int i=1;i<=n-k+1;i++) { if((c[i]+sum)%2==1) { cnt++; f[i]=1; } sum+=f[i]; if(i-k+1>=1)sum-=f[i-k+1]; } for(int i=n-k+2;i<=n;i++) { if((c[i]+sum)%2==1)return 2147483647; if(i-k+1>=1)sum-=f[i-k+1]; } return cnt; } char ss[10]; int main() { scanf("%d",&n); int ansk=1,ansm=0; for(int i=1;i<=n;i++) { scanf("%s",ss+1); if(ss[1]=='F') c[i]=0; if(ss[1]=='B'){c[i]=1;ansm++;} } for(int k=2;k<=n;k++) { int m=solve(k); if(m<ansm)ansk=k, ansm=m; } printf("%d %d\n",ansk,ansm); return 0; }
pain and happy in the cruel world.