【反转】POJ3276

Posted on 2018-05-14 20:43  som_nico  阅读(135)  评论(0编辑  收藏  举报

Face The Right Way

题意:N个牛 每个都有一定的方向 B背对 F表示头对着你 给你一个装置 每次可以选择连续的K个牛反转方向 问你如何选择K 使得操作数最少 k也应尽量小. 

例子:   N=7   BBFBFBB   (F:前面   B:后面)    (红色的为要反转的)   此出K=3  M=3  

B B F B F B B

F F B B F B B

F F F F B B B

F F F F F F F   成功了

 

如果用暴力的方法的话:   考虑N头牛的话最坏情况下要进行N-k+1次反转操作。

for(k=1;k<=N;k++)

for(i=1;i<=N;i++)

for(j=i;j<=i+k;j++)

因此复杂度为n^3  。

 

反转算法:

定义 f[i]:区间[i,i+k-1]进行反转的话就为1,否则为0

区间反转部分很好优化: 
在考虑第i头牛时候,如果i1j=(iK+1)f[j]∑j=(i−K+1)i−1f[j]和为奇数,就说明此时这个牛方向与最初相反。 
由于 
ij=(i+1)K+1f[j]∑j=(i+1)−K+1if[j]=i1j=(iK+1)f[j]∑j=(i−K+1)i−1f[j]+f[i]-f[i-K+1]

所以这个每一次都可以用常数算出来,时间复杂度O(n^2)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=5000+10;
int f[N],dir[N],n;
int solve(int k){
   int cnt=0,sum=0;//sum为f的和
   memset(f,0,sizeof(f));
   for(int i=1;i<=n-k+1;i++){
    if((dir[i]+sum)%2){
         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((dir[i]+sum)%2) return n+1;
      if(i-k+1>=1) sum-=f[i-k+1];
   }
   return cnt;
}

int main(){
   while(~scanf("%d",&n)){
       for(int i=1;i<=n;i++){
          char c;scanf(" %c",&c);
          if(c=='B') dir[i]=1;
       }
       int ansk,ansm=n,t;
       for(int i=1;i<=n;i++){
          t=solve(i);
          if(t<ansm){
              ansm=t;ansk=i;
          }
       }
       printf("%d %d\n",ansk,ansm);
   }
}

 

参考:
常用技巧精选.反转(开关问题) poj3276 3279 3185 1244

开关问题--反转 poj 3276