poj 3276 Face The Right Way

原题:
Face The Right Way
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 6751 Accepted: 3141
Description

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.

Input

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.
Output

Line 1: Two space-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), and finally (5,6,7)
Source

USACO 2007 March Gold

中文:

给你n头牛,有的牛头向前,有的牛头向后。你可以对一个区间中的连续的k头牛进行翻转,即向前的变为向后,向后的变为向前。现在问你最少翻转多少次能把所有牛头都向前?输出最少次数情况下的k和次数m。

//#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<string>
#include<string.h>

using namespace std;
typedef long long ll;

const int maxn=5005;

int dir[maxn],f[maxn],n;

int calc(int k)
{
    memset(f,0,sizeof(f));

    int res=0,sum=0;

    for(int i=0;i+k-1<n;i++)
    {
        if((sum+dir[i])%2!=0)
        {
            res++;
            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((sum+dir[i])%2!=0)
            return -1;
        if(i-k+1>=0)
            sum-=f[i-k+1];
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n)
    {
        string s;
        memset(dir,0,sizeof(dir));
        for(int i=0;i<n;i++)
        {
            cin>>s;
            if(s=="B")
                dir[i]=1;
            else
                dir[i]=0;

        }
        int k=1,m=n;
        for(int i=1;i<=n;i++)
        {
            int tmp=calc(i);
            if(tmp>=0&&tmp<m)
            {
                m=tmp;
                k=i;
            }
        }
        cout<<k<<" "<<m<<endl;
    }

    return 0;
}

思路:

挑战程序设计竞赛上的例题,开关翻转问题。

此题要求时间复杂度在O(n^2)及以下才能通过。

要明白此题,首先要明确两点。

第一,先对哪个区间翻转,后对哪个区间翻转,对结果是没有影响的。例如,先对[1,3]进行翻转,在对[2,4]进行翻转与先对[2,4]进行翻转,和先对[1,3]进行翻转没有区别。

第二,同一个区间进行两次以上的翻转是多余的。

那么,可以枚举翻转的区间长度k,从第1头牛开始,如果这头牛头朝后,那么这头牛肯定要翻转,即将[i,i+k-1]区间的牛翻转,一直翻转到第n-k+1头牛。

枚举翻转的区间k,同时在枚举每一头牛,同时在进行翻转,时间复杂度O(n^3),需要优化。

从翻转的过程入手,每次枚举翻转长度k时,建立一个长度为k的滑动窗口sum,这个滑动窗口记录在这个区间执行了多少次翻转,如果执行了奇数次翻转,那就说明这个区间里的牛和之前的状态是相反的,即头朝前变成了头朝后。那么,再根据给定的牛的状态,就可以判断这头牛现在的朝向,从而是否决定要将这头牛以及后面的k-1头牛进行翻转了。

上面的过程用一个f[i]来记录,每次记录当前牛是否翻转。sum用来维护一个长度k的滑动窗口,里面记录窗口中f[i]的累加和,通过判断奇偶性来判断牛的状态和之前是否相同。

很经典的题目,很巧妙的做法哦!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值