POJ3276—Face The Right Way 【常用技巧—反转(开关问题)】
Face The Right Way
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 5944 Accepted: 2749
Description
Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many
of them are facing forward, like good cows. Some of them are facing
backward, though, and he needs them all to face forward to make his
life perfect.Fortunately, FJ recently bought an automatic cow turning machine.Since he purchased the discount model, it must be irrevocably presetto turn K (1 ≤ K ≤ N) cows at once, and it can only turn cows that are all standing next to each other in line. Each time the machine isused, it reverses the facing direction of a contiguous group of K cows in the line (one cannot use it on fewer than K cows, e.g., at theeither end of the line of cows). Each cow remains in the same location as before, but ends up facing the opposite direction. A cow that starts out facing forward will be turned backward by the machine and vice-versa.
Because FJ must pick a single, never-changing value of K, please help him determine the minimum value of K that minimizes the number of operations required by the machine to make all the cows face forward.Also determine M, the minimum number of machine operations required to get all the cows facing forward using that value of K.
Input
Line 1: A single integer: N Lines 2..N+1: Line i+1 contains a single character, F or B, indicating whether cow i is facing forward or backward.
Output
Line 1: Two space-separated integers: K and M
Sample Input
7
B
B
F
B
F
B
B
Sample Output
3 3
Hint
For K = 3, the machine must be operated three times: turn cows (1,2,3), (3,4,5), and finally (5,6,7)
Source
USACO 2007 March Gold
大意(来源《挑战程序设计竞赛》)
思路
由于题目要求K与M的最小值,显然是不能同时求出它们的值的(注意M值优先,即在M最小的基础上求K的最小值)。所以我们可以利用枚举将K值固定下来,对于每一个K值,求出最小的M,再进行比较。
那么问题来了,如何对于一个特定的K值求出最小的M呢?搜索显然是不现实的,TLE无疑。那我们就分析一下这个反转的特性。首先,交换反转顺序对结果无影响。其次,对同一个区间进行两次及以上的反转是毫无意义的。所以问题就转化为了求需要被反转的区间的集合。
f[i]不仅表示了这个区间的反转情况,还表示了第i头牛是主动反转的。
在操作完之后再检查剩下的牛,由于剩下的牛是不能自主反转的,如果后面剩下的牛有向后的话,即K值是不合法的
代码实现
//本质是枚举题
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 5000
int N;
int dir[MAXN+5],f[MAXN+5];
//f[i]:区间[i,i+K-1]反转了为1,否则为0
//同时也表示i是“主动”反转
int calc(int K)
{
memset(f,0,sizeof(f));
int res=0,sum=0;
for(int i=0;i+K<=N;i++)
{
if((dir[i]+sum)%2!=0)
res++,f[i]=1;
sum+=f[i];
if(i-K+1>=0)
sum-=f[i-K+1];
}
//检查剩下的牛
for(int i=N-K+1;i<N;i++)
{
if((dir[i]+sum)%2!=0)
return -1;
if(i-K+1>=0)
sum-=f[i-K+1];
}
return res;
}
int main()
{
scanf("%d",&N);
char a[5];
for(int i=0;i<N;i++)
{
scanf("%s",a);
if(a[0]=='B') dir[i]=1;
else dir[i]=0;
}
int K=1,M=N;
for(int k=1;k<=N;k++)
{
int m=calc(k);
if(m>=0&&M>m)
M=m,K=k;
}
printf("%d %d\n",K,M);
}