【题目链接】:click here~~
【题目大意】:N头牛排成一列1<=N<=5000。每头牛或者向前或者向后。为了让所有牛都 面向前方,农夫每次可以将K头连续的牛转向1<=K<=N,求操作的最少 次数M和对应的最小K。
【思路】:由于交换区间翻转顺序对结果没影响,所以从左往右对于需要 翻转的牛进行反转,同时记录对该区间其他牛的影响即cal中的sum, 对于最后部分无法翻转的区间检查是否有反向牛,若有则方案失败。此题思想值得细细思考,常常有一种无限状态,化为有限状态。
代码:
/**************
*POJ 3276 (尺取法)
*Face The Right Way
*
**************/
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int N=5005;
int t,n,m;
int f[N]; //翻转状态,1翻转,0不翻转
int dir[N]; // 输入状态 F<>0 B<>1
char op[2];
int calc(int k){ //枚举k ,翻转顺序从左到右
memset(f,0,sizeof(f));
int i,res=0; // 连续翻转的次数
int sum=0;
for(i=0; i+k<=n; ++i){
if((dir[i]+sum)&1){ //翻转奇数次改变状态,偶数次不改变
res++;
f[i]=1;
}
sum+=f[i]; //尺取法
if(i-k+1>=0) sum-=f[i-k+1];
}
for(; i<n; ++i){//判断剩下的是否有反向牛
if((dir[i]+sum)&1) return -1;
else if(i-k+1>=0) sum-=f[i-k+1];
}
return res;
}
void solve(){
int K=1,M=n;
for(int i=1; i<=n; ++i){
int m=calc(i);
if(m>0&&m<M){
M=m;
K=i;
}
}
printf("%d %d\n",K,M);
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=0; i<n; ++i){
scanf("%s",op);
if(op[0]=='F') dir[i]=0;
else dir[i]=1;
}
solve();
}
return 0;
}