反转问题

来源:互联网 发布:网络维护外包合同 编辑:程序博客网 时间:2024/06/05 14:08

反转问题

poj3276

题意:
N头牛排成一列,每头牛或者朝前,或者朝后。为了让所有牛都面向前方,农夫约翰买了一台自动转向的机器。这个机器在购买时就必须设定一个数值K,机器每操作一次恰好使K头连续的扭转向。请求出为了让所有的牛都能面向前方所需要的最少操作次数M和对应的最小K。

限制条件:
1<=N<=5000

思路:
首先,可以确定翻转顺序对结果是没有影响的。其次,可以知道对同一个区间进行两次以上的反转是多余的。因此,我们可以从最左边开始,如果最左边那个需要反转就反转K头连续的牛,在判断第二个是否需要反转…..,这样写的话,复杂度有点高,最差o(n^3)不能解决这个问题。区间反转部分还算很容易优化的。
f[i]:=区间[i,u+K-1] 进行了反转的话就为1,否则为0
这样在考虑第i头牛时,如果

i−1
∑ f [ j] 为奇数的话,则着头牛的方向玉开始方向是相反的(就是看他之前被反转了多少次)。否则方向不
j=i−K+1
变。
由于:
∑(j=(i+1)-K+1,i) f[j] = ∑(j=i-K+1,i-1)f[j] + f[i] - f[i-K+1]
所以这个和每一次都可以在常熟时间内计算出来,复杂度就降为O(N^2)
AC代码:

//// Created by luozujian on 17-12-2.//#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<queue>#include<vector>using namespace std;const int maxn = 5000+5;int dir[maxn];     //牛的方向(0:F,1:B)int f[maxn];   //区间[i,i-k+1]是否进行反转int n;//固定k 求最小的操作数  无解返回-1int calc(int k){    memset(f,0,sizeof(f));    int res = 0;    int sum = 0;    for(int i=0;i+k<=n;i++)    {        if((sum+dir[i])%2 !=0)        {             //牛面朝后方。            f[i] = 1;            res++;        }        sum+=f[i];        if(i-k+1 >= 0)        {            sum-=f[i-k+1];        }    }    //检查剩下的牛是否有面朝后方的情况    for(int i=n-k+1;i<n;i++)    {        if((sum+dir[i])%2 !=0)        {            return -1;   //无解        }        if(i-k+1>=0)        {            sum-=f[i-k+1];        }    }    return res;}void solve(){    int K = 1,M = n;    for(int k=1;k<=n;k++)    {        int m = calc(k);        if(m>0 && M>m)        {            K = k;            M = m;        }    }    printf("%d %d\n",K,M);}int main(){    scanf("%d",&n);    for(int i=0;i<n;i++)    {        char str[2];        scanf("%s",str);        printf("%c\n",str[0]);        if(str[0] == 'B') dir[i] = 1;        else dir[i] = 0;    }    solve();    return 0;}