Face The Right Way
题目大意:给你一个字符串,包含B,F,两种字母,然后每次可以连续翻转K个,问每次翻转几个,就是问一个值,可以翻转最少次数,使字符串全部变为F
思路:刚开始,一看这么简单,遍历1-n的K,枚举不就行了,呵呵呵,md肯定TLE,每枚举一次,时间复杂度大概是O(2^n) n最大5000 2000ms大概是10的9次方,也就是2的32次方左右,肯定超时,
如果是对于同一个区间的话,翻转两次 == 没有翻转 ,如果是我们只需要求,有多少个区间需要翻转不就好了,然后如果从最左端开始,如果最左这个需要翻转,那么我们就翻转从当前到后面共K个,然后下一个再下一个,但是每次翻转,都会循环K次,这里有了技巧,如果我们拿过来一个数组,f[i] 如果是1就是代表从i到后面k个都翻转,是0就是不翻转,就不要循环了,具体讲解写注释里面了,更清楚一些。
#include <iostream>
#include <cstring>
using namespace std;
char arr[5555]; //多余的不用管
int f[5555]; //上述,
int dir[5555]; //用整型数组表示字符串,0就是F 1就是B
int n;
int dfs(int k)
{
int res = 0, sum = 0; //sum很重要
memset(f, 0, sizeof f);
for(int i = 0; i + k <= n; ++i) //从i到后面i+k-1是翻转区间 一共k个
{
if((dir[i] + sum) % 2 != 0)
//什么意思呢,刚开始sum0对吧,如果dir[i]是1的话就是B,代表需要翻转
//
{
res++; //翻转一次+1
f[i] = 1; //当前区间标记为翻转
}
sum += f[i];
//既然从i -- i+k-1都翻转了,那么从i+1到i+k-1都是翻了的,这样的话,
//sum + 1 这里1就是f[i] 就是翻的时候,这样sum是1对吧,
//然后后面dir[i]本来是0的就要变成1对吧,但是咱又不想循环,这样当前for循环到
//下个i的时候,会判断dir[i] + sum 因为翻转了,sum是1 对于后面的如果dir[i]是0
//这时候加起来是1 就能代表需要翻转了,sum的作用就是用了代理翻转,记录一下
if(i - k + 1 >= 0)
sum -= f[i-k+1];
//这里又很重要,对于一个i看他翻转了没是不是要看它前面隔了k个的那个,
//sum只能管到一个i到前面k个,那当前循环完了下一个i就不会被之前那个sum
//管到了,那么就得减去之前的翻转情况,自己模拟一下就知道了
}
for(int i = n-k+1; i < n; ++i)
{
if((dir[i] + sum) % 2 != 0) return -1;
//对于后面k-1个是不会作为区间第一个翻转的,前面的都保证了是F 所以看这几个
//有B就不行-1
if(i - k + 1 >= 0)
{
sum -= f[i-k+1]; //同上
}
}
return res;
}
int main()
{
cin >> n;
for(int i = 0; i < n; ++i)
{
char ch;
cin >> ch;
if(ch == 'B') dir[i] = 1; //初始化
else dir[i] = 0;
}
int ans = 0x3f3f3f3f;
int temp;
for(int k = 1; k <= n; ++k) //遍历所有的k,求最小值
{
int a = dfs(k); //返回值就是当前k要满足最后条件需要翻转的最小次数
if(a < ans && a != -1)
{
temp = k;
ans = a;
}
}
cout << temp << ' ' << ans << endl;
return 0;
}