您的位置:首页 > 其它

POJ 3276 Face The Right Way (开关问题 两种做法)

2016-09-11 19:07 381 查看
传送门:POJ3276 Face The Right Way

描述:

Face The Right Way

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 4249 Accepted: 1959
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 preset to 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 is used, 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 the either 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尽可能小。

思路一:
设发f[i]为区间[i,i+k-1]是否进行反转 ,反转的话为1,否则为0,在考虑第i头牛时  ,如果     为奇数的话则这头牛方向与其实方向相反,否则方向不变

由于这个和可以在常数时间计算出来,变量只有k,i这两个,复杂度为O(n^2)

具体说明可参考挑战那本书

//G++ 329ms 704k
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=5555;

int n;
int a[maxn],f[maxn];//牛的方向 区间[i,i+k-1]是否进行反转

//固定l,求对应的最小操作回数
//无解的话返回-1
int calc(int k){
memset(f, 0, sizeof(f));
int res=0;
int sum=0;//f的和
for(int i=0; i+k<=n; i++){
//计算区间[i,i+k-1]
if((a[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((a[i]+sum)%2 != 0){
//无解
return -1;
}
if(i-k+1>=0)sum-=f[i-k+1];
}
return res;
}

int main(){

while(~scanf("%d",&n)){
int K=1,M=n;
char ch;
for(int i=0; i<n; i++){
scanf(" %c",&ch);
if(ch=='F')a[i]=0;
else a[i]=1;
}
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);
}
return 0;
}


思路二:

首先不要看错题意了,不是求最小K,不要二分。而且反转区间长度一定是K,小于K是不能反转的。

很明显得枚举K(1...n),并且有以下反转思路:

①从第一头牛开始,如果朝前,不管了。看下一头牛,如果朝后反转K长度区间.....一直扫到区间结束。

②第一趟结束后,如果不符合要求,继续重复①,直到所有牛都朝前。

这样复杂度是O(n^3),5000*5000*5000,标准TLE。

 

其实确定反转次数只需要扫一趟就行了,没有必要来回多趟。O(n^2)就能解决,这里借鉴了tmeteorj的依赖关系法,非常简洁。

它的思路是这样的:

f[i]保存的当前牛与前一头牛的关系,不同1,同0。其中设置一个0牛,方向为F。

这样,如果f[i]=1,则表示[i-1,i+k-1]这个区间需要反转,其中f值变化的只有f[i]和f[i+k]。中间的值没有变化。

对于每个K,从1扫到n-k+1,如果f[i]=1则进行反转操作,反转之后变化的部分立刻反馈,这样当处理i+1时,就能保证当前状态是处理i+1的最后一趟的状态。

原因很简单,在O(n^3)的方法里,我们来回扫,不过是把值来回重复循环,毫无意义。使用这种关系依赖法之后,就可以避免这些毫无意义的循环。

 对于n+k+2~n的部分,只要出现需要反转的,则本次K是无效的。继续看下一个K。

否则,更新一下ansm和ansk。

这个做法比思路一稍快~
//G++ 266ms 700k
#include <iostream>
#include <cstdio>
#include<cstring>
using  namespace std;
const int inf=0x3f3f3f3f;

int f[5005],now[5005],n,ansm,ansk;
int  main(){
char key,last='F';
ansm=inf;
scanf("%d",&n);
for(int i=1; i<=n; i++){
scanf(" %c",&key);
if(key!=last)f[i]=1;
last=key;
}
for(int k=1; k<=n; k++){
memcpy(now, f, sizeof(f));
int cnt=0;
for(int i=1; i<=n-k+1; i++)
if(now[i]){ cnt++; now[i+k]^=1;}
for(int i=n-k+2; i<=n; i++)
if(now[i]){ cnt=inf; break;}
if(cnt<ansm){ ansm=cnt; ansk=k;}
}
printf("%d %d\n",ansk, ansm);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  开关问题