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.
N头牛排成一列\(1<=N<=5000\)。每头牛或者向前或者向后。为了让所有牛都 面向前方,农夫每次可以将K头连续的牛转向\(1<=K<=N\),求操作的最少次数M和对应的最小K。
输入格式
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.
第一行一个整数N
第2行至\(N+1\)行是一个字符,F或B,表示向前或向后.
输出格式
Line 1: Two space-separated integers: \(K\) and \(M\)
一行两个数字,\(K\)和\(M\)
输入样例
7
B
B
F
B
F
B
B
输出样例
3 3
题解
很容易想到暴力解法,三层循环,一层枚举\(k\),一层遍历从前往后遍历数组,一遇到向后的牛就一层循环反转后面长度为k的数组,时间复杂度\(O(n^3)\),显然不行,那么就需要优化.
看最后一层循环,可以使用差分优化,每次反转就将次数+1,由于反转两次相当于没有反转,所以就可以根据次数和原本的状态推出反转后的状态.
不需要开一个前缀和数组,因为从前往后遍历,只需要定义一个sum
变量存储前缀和,一边累计前缀和一边修改差分数组即可.
代码
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 5010, INF = 1e9;
int n, r, a[maxn], f[maxn];
int check(int k) {
memset(f, 0, sizeof(f));
int sum = 0, ans = 0;
for (int i = 1; i <= n; ++i) {
if (~(a[i] + sum) & 1) { // 如果 a[i] + flag 为偶数
if (i + k - 1 > n) return INF + 1; //超过限制, 返回最大值以保证答案不更新
f[i] ++, f[i + k - 1] --, ans++;
}
sum += f[i];
}
return ans;
}
int main() {
scanf("%d", &n);
char status;
for (int i = 1; i <= n; i++) getchar(), a[i] = getchar() == 'B' ? 0 : 1;
int maxv = INF, ansk = n;
for (int k = 1; k <= n; k++) if ((r = check(k)) <= maxv) maxv = r, ansk = k;
printf("%d %d\n", ansk, maxv);
return 0;
}
附
又到了水的时间
介绍一些我觉得有意思的小技巧,可以让你的代码看起来更简洁难懂
- 判断一个数字奇偶
正常操作自然是模2看结果是0还是1,但是你是否感觉加一个==0
或==1
太累赘呢?如果想要a
为奇数时表达式为真,可以直接使用a%2
.如果想要偶数时表达式为真,可以使用!(a%2)
.
但是后面这个这个又不美观了,要知道if
,while
等语句的条件也是要加括号的,如果想要偶数时表达式为真,代码可能会变成这样:
if(!(a%2)){
//do something
}
双层括号套一起,太丑了,这时候建议使用位运算,我们要知道一个整数为奇数时,其二进制表示最低位为1,反之为0,那么对这个整数与1取最低位,就可以判断奇偶
如果想要a
为奇数时表达式为真,可以使用a&1
.如果想要偶数时表达式为真,可以使用~a&1
同样是上面那种情况
if(~a&1){
//do something
}
看起来是不是更清爽让人看不懂了呢?
- 合理使用
getchar()
由于scanf
和cin
的返回值不是输入值,所以不能直接放在条件表达式里,但是getchar()
却不一样,像这种输入字符表示状态的题,可以使用getchar()
来获取输入
getchar(), a[i] = getchar() == 'B' ? 0 : 1;
第一个getchar()是读取回车符,然后用三元运算符判断输入,B为0,F为1
- 当条件表达式和语句块内都使用函数返回值
由于当更新答案时,还要执行其它操作,不能用max
,min
函数,所以需要使用if语句
这里的情况就是
int r=check(k);
if (r <= maxv) maxv = r, ansk = k;
第一行看起来就不爽,想干掉它,但是如果改成:
if (check(k) <= maxv) maxv = check(k), ansk = k;
调用两次函数浪费了时间显然不能选
这时候注意,赋值语句也是有返回值的!其实也能算表达式?
所以你可以这么写(r变量先定义好):
if ((r = check(k)) <= maxv) maxv = r, ansk = k;