POJ 3276 尺取法

看的挑战程序竞赛上面的  于是想自己先做一做上面的题目  讲到尺取法这里  先看了一个比较简单的例题
紧接着就看到了这一题 关上书  准备自己YY出来.....
结果自己的yy方法 跟尺取法 完全没关系  亏我自己yy

首先  O(n^3) 标准的TLE姿势躺好
然后  改进....改进...
我把第三重循环 从模拟点 改成模拟区间 加入到queue中 就可以了 1600ms+ 姿势还好 过了

其实我的代码 还是很多需要改进的地方 不过方法差不多是这样
就懒一懒……

上代码Time:

#include<cstdio>
#include<iostream>
using namespace std;
#include<queue>
const int maxm=5005;
const int inf=0x3f3f3f3f;
int a[maxm];
int b[maxm];
queue<int>que;

int getfilp(int x)
{
    int s=que.size();
    if(!s)return 0;
    while(!que.empty())
    {
        int temp=que.front();
        if(x>temp)que.pop();
        else return (int)que.size();
    }
    return que.size();
}
inline int min(int aa,int bb)
{
    return aa>bb?bb:aa;
}
int main()
{
    int n;
// freopen("in1.txt","r",stdin);
    while(scanf("%d",&n)!=-1)
    {
        int m;
        int sum_=0;
        for(int i=0; i<n; i++)
        {
            char t_;
            cin>>t_;
            if(t_=='B')
            {
                a[i]=0;
            }
            else a[i]=1;///F
            sum_=0;
        }
        if(sum_==n){
            printf("0 0\n");continue;
        }
        int ans=inf,ansk=inf;
        for(int k=1; k<=n; k++)
        {
            while(!que.empty())que.pop();
            int f_=n-k;
            m=0;
            for(int i=0; i<n; i++)
            {
                if(i==f_)
                {
                    int sum=0;
                    for(int z=i; z<n; z++)
                    {
                        int coun_=getfilp(z);
                        if(coun_&1)sum+=(!a[z]);
                        else sum+=a[z];
                    }
                    if(!sum||(sum==k))
                    {
                        if(ans>=(m+(sum==0)))
                        {
                            if(ans==(m+(sum==0)))
                                ansk=min(ansk,k);
                            else ansk=k;
                            ans=(m+(sum==0));
                        }
                    }
                    break;
                }
                int coun=getfilp(i);
                if((coun&1)==a[i]) ///B
                {
                    m++;
                    que.push(i+k-1);
                }
            }
        }
        if(ans==0)ansk=0;
        printf("%d %d\n",ansk,ans);
    }
    return 0;
}

下面 我们再看看 传说中的 尺取法!

也不知道是谁的 忘记了 反正这代码不是我yy的

上代码:

//思路: 从第一个开始找如果当前为B 就需要改变 改变i到i+k-1的位置
//一直找到n-k+1处 之后判断从n-k+2到n是否满足了题意 注意不要真的去修改
//用一个sum值标记前面k-1个修改了多少次用来决定当前的是否要修改 利用尺取法 即前后推进
#include<stdio.h>
#include<string.h>
int a[5100],n,flag[5100];
int solve(int k)
{
    int i;
    memset(flag,0,sizeof(flag));//flag[i]表示区间[i,i+k-1] 是否需要翻转
    int sum=0,cnt=0;//前k-1个转变的次数
    for(i=1;i<=n-k+1;i++)//sum记录走到当前i,其前面k-1个翻转了多少次
    {
        if(i-k>=1)
        {
            sum-=flag[i-k];
        }
        if(a[i]==0&&sum%2==0)//如果是B 且前面翻转了偶数次 仍旧需要翻转
        {
             flag[i]=1;
             sum+=flag[i];
             cnt++;
        }
        else if(a[i]==1&&sum%2==1)//如果是F 且前面翻转了奇数次
        {
            flag[i]=1;
            sum+=flag[i];
            cnt++;
        }

       // printf("i=%d flag[i]=%d\n",i,flag[i]);
    }

        for(i;i<=n;i++)
        {
           if(i-k>=1)
           {
              sum-=flag[i-k];
           }
           if(sum%2==0&&a[i]==0) return -1;
           else if(sum%2==1&&a[i]==1) return -1;
        }
        return cnt;


}
int main()
{
    int i,k,mn;
    char s[2];
// freopen("in1.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        mn=100010000;
        for(i=1;i<=n;i++)
        {
            scanf("%s",s);
            if(s[0]=='B') a[i]=0;
            else if(s[0]=='F') a[i]=1;

        }
        k=1;
        for(i=1;i<=n;i++)
            {
                int mid=solve(i);
                //printf("k=%d,cnt=%d\n",i,mid);
                if(mid==-1) continue;
                if(mn>mid) {mn=mid;k=i;}
            }
        printf("%d %d\n",k,mn);
    }
    return 0;
}

核心思想 把本来模拟每一个点的 变成直接模拟一个区间 方法很巧妙 比起我的垃圾方法来说TAT

通过首尾不断推进 动态更新sum的值 得到反转的次数 再&1 就知道目前的状态了

复杂度肯定没我的高 我的复杂度 求队列的size我觉得可能很耗时间

你可能感兴趣的:(POJ 3276 尺取法)