题意: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头牛时候,如果∑i−1j=(i−K+1)f[j]∑j=(i−K+1)i−1f[j]和为奇数,就说明此时这个牛方向与最初相反。
由于
∑ij=(i+1)−K+1f[j]∑j=(i+1)−K+1if[j]=∑i−1j=(i−K+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
原文地址:https://www.cnblogs.com/Kohinur/p/9038032.html
时间: 2024-10-06 00:53:53