poj 3276(反转)
参考资料:
[1]:挑战程序设计竞赛
先献上AC代码,题解晚上再补
题意:
John有N头牛,这些牛有的头朝前("F"),有的朝后("B"),John想让所有的牛头都超前。
现在,John得到了一个机器,每次可以让连续的 K 头牛转向,问最少需要用多少次(M)机器可以使所有的牛头都超前?
题解:
变量解释:
dir[i] : dir[i]=0 -> 第i头牛面朝前;dir[i]=1 -> 第i头牛面朝后
f[i] : f[i]=0 -> 在第i头牛出不进行反转操作;f[i]=1 -> 在第i头牛出进行反转操作
首先,需要明白两点:
(1):交换区间反转的顺序对结果是没有影响的。
(2):对同一个区间进行两次以上的反转是多余的。
因此,问题就转化成了求需要被反转的区间的集合。
定义 k : 每次需要反转的牛的个数(1 <= k <= N)
i : 第 i 头牛(1 <= i <= N-k+1,初始 i = 1)
sum : 受前面反转影响([i-k+1,i-1]),来到第 i 头牛,总共反转的次数
res : 存储反转次数
(1):对于第i头来说,如果它是面朝后的,则需要一次反转使其面朝前,而之后的反转区间指定不包含此牛。
(2):判断第i头牛是否需要反转,如果需要,f[i]=1,res++;i++;
(3):重复(2)过程,直到 i > N-k+1为止
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=5e3+50; 5 6 int N; 7 int dir[maxn]; 8 int f[maxn]; 9 10 int Calculate(int k) 11 { 12 int res=0; 13 int sum=0; 14 for(int i=1;i <= N-k+1;++i) 15 { 16 if(i-k > 0)//当前的i只受到区间 [i-k+1,i-1] 反转的影响,所以需要去除i-k对i的反转影响 17 sum -= f[i-k]; 18 if((dir[i]+sum)%2 != 0)//判断dir[i] 是否为偶数,偶数代表面朝前 19 f[i]=1,res++; 20 sum += f[i]; 21 } 22 for(int i=N-k+2;i <= N;++i)//检查后 k-1头牛是否全都面朝前 23 { 24 if(i-k > 0)//解释同上 25 sum -= f[i-k]; 26 if((dir[i]+sum)%2 != 0) 27 return -1; 28 } 29 return res; 30 } 31 void Solve() 32 { 33 int K=1,M=N; 34 for(int k=1;k <= N;++k)//每次反转 k 头牛 35 { 36 int m=Calculate(k); 37 if(m != -1 && m < N) 38 K=k,M=m; 39 } 40 printf("%d %d\n",K,M); 41 } 42 43 int main() 44 { 45 scanf("%d",&N); 46 for(int i=1;i <= N;++i) 47 { 48 getchar(); 49 char ch=getchar(); 50 dir[i]=(ch == 'F' ? 0:1); 51 } 52 Solve(); 53 }