开关问题1(poj 3276)

来源:互联网 发布:java 字符串相等 编辑:程序博客网 时间:2024/06/03 13:47

Face The Right Way

Time Limit: 2000MS

Memory Limit: 65536K

Description

Farmer John has arranged his N (1≤ N ≤ 5,000) cows in a row and many of them are facingforward, like good cows. Some of them are facing backward, though, and he needsthem all to face forward to make his life perfect.

Fortunately, FJ recently bought anautomatic cow turning machine. Since he purchased the discount model, it mustbe irrevocably preset to turn K (1 ≤ K ≤ N) cowsat once, and it can only turn cows that are all standing next to each other inline. Each time the machine is used, it reverses the facing direction of acontiguous group of K cows in the line (one cannot use it onfewer than K cows, e.g., at the either end of the line ofcows). Each cow remains in the same *location* as before, butends up facing the *opposite direction*. A cow that starts outfacing 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 minimumvalue of K that minimizes the number of operations required bythe machine to make all the cows face forward. Also determine M,the minimum number of machine operations required to get all the cows facingforward using that value of K.

Input

Line 1: A singleinteger: 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: Twospace-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), andfinally (5,6,7)

 

题意

给定一只含有’B’(Back),’F’(Front)的字符串(长度小于5000)代表每个位置上的牛的朝向,可以对连续的K头牛进行位置翻转操作,求使得所有牛都面向正面的最少操作数对应的K以及最小操作数M


思路

K=1时一定有解,所以不存在无解情况,应当把K从1到N尝试并求解,记录相应的最小操作数。对于某个K,求解M的思路如下:

把牛的朝向用整数0(向前)和1(向后)来表示存入数组dir中,求解时从左往右考虑,如果当前最左端的牛(下标为i)朝向为0,则对应的区间[i,i+K-1]不用翻转,反之必须翻转。模拟这个过程即可求解,但时间复杂度高。我们用一个数组f来对其做优化,f[i]表示从区间[i,i+K-1]是否翻转,是为1,否则为0。在判断当前区间[i,i+K-1]是否需要翻转时,如果sum(f[i-K+1],f[i-1])为奇数,那么i的方向与原方向相反,反之相同。进一步可简化为如果 (sum + a[i]) % 2 == 1 那么就翻转a[i] (sum为奇数,a[i]==0,0变成了1;sum为偶数,a[i]==1,1还是1),否则不翻转(sum为奇数,a[i]==1,1变成0;sum为偶数,a[i]==0,0还是0)。并且可以用递推的思想计算sum,在代码中有所体现。递推式:sum(f[i-K+1],f[i-1]) = sum(f[i-K],f[i-2])+ f[i-1] – f[i-K+1]


代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int maxn = 5050;int n;char c;int dir[maxn];int f[maxn];int calc(int k) {int res = 0;//记录操作次数int sum = 0;memset(f, 0, sizeof(f));for (int i = 0; i <= n - k; i++) {//最后一个可能翻转的区间[n-k,n-1]if ((sum + dir[i]) & 1) {res++;f[i] = 1;}sum += f[i];if (i - k + 1 >= 0)sum -= f[i - k + 1];}//检查最后的区间[n-k+1,n-1]里的朝向for (int i = n - k + 1; i < n; i++) {if ((sum + dir[i]) & 1) {return -1;//无解}if (i - k + 1 >= 0)sum -= f[i - k + 1];}return res;}void solve() {int ansm = n, ansk;for (int k = 1; k <= n; k++) {int m = calc(k);if (m != -1 && m < ansm) {ansm = m;ansk = k;}}printf("%d %d\n", ansk, ansm);}int main() {while (scanf("%d", &n) == 1) {for (int i = 0; i < n; i++) {scanf(" %c", &c);if ('F' == c) dir[i] = 0;else dir[i] = 1;}solve();}return 0;}


原创粉丝点击