题意:
N头牛排成了一列。每头牛或者向前或者向后。为了让所有的牛都面向前,农夫约翰买了一台自动转向的机器。这个机器在购买时就必须设定一个数值K,机器每操作一次恰好使K头连续的牛转向。请求出为了让所有的牛都能面向前方需要的最少的操作次数M和对应的最小的K。
分析:
首先,交换区间反转的顺序对结果是没有影响的。此外,可以知道对同一个区间进行两次以上的反转是多余的,由此,问题就转化成了求需要被反转的区间的集合。于是我们先考虑一下最左端的牛。包含这头牛的区间只有一个,因此如果这头牛面朝前方,我们就能知道这个区间不需要反转。
反之,如果这头牛面朝后方,对应的区间就必须进行反转了。而且在此之后这个最左的区间就再也不需要考虑了。这样一来,通过首先考虑最左端的牛,问题的规模就缩小了1.不断的重复下去,就可以无需搜索求出最少所需的反转次数了。
详细可以看挑战里的分析。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<cctype>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<iomanip>
#include<sstream>
#include<limits>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N= 2000;
const int maxn = 1e4+10;
const int maxm = 1e7+10;
const int MOD = 1e9+7;
int dir[maxn],f[maxn]; //dir方向
int n;
int work(int k) //精华部分
{
memset(f,0,sizeof(f));
int cnt = 0,sum = 0;
for(int i = 0; i + k <= n; i++)
{
if ((sum + dir[i])%2 != 0)
{
cnt++;
f[i] = 1;
}
sum += f[i];
if (i -k +1 >= 0) sum -= f[i-k+1];
}
for(int i = n-k+1; i < n ; i++) //判断是否合法
{
if ((dir[i]+sum)%2 != 0) return -1;
if (i-k+1 >= 0) sum -= f[i-k+1];
}
return cnt;
}
int main(){
#ifdef LOCAL
freopen("C:\\Users\\lanjiaming\\Desktop\\acm\\in.txt","r",stdin);
//freopen("output.txt","w",stdout);
#endif
//ios_base::sync_with_stdio(0);
while(scanf("%d",&n)!=EOF)
{
for(int i = 0; i < n; i++)
{
char ch;
cin>>ch;
if (ch == 'B') dir[i] = 1;
else dir[i] = 0;
}
int k = 1, m = n;
for(int i = 1; i <= n; i++) //枚举长度k
{
int tt = work(i);
if (tt >= 0 && tt < m) m = tt,k = i;
}
printf("%d %d\n",k,m);
}
return 0;
}