尺取法总结

最近在做尺取法,总结一下

尺取法,说白了就是一个st,ed;这两个左右端点(起点、终点)在运动,适用条件就是要求一段连续的区间,

并且st(左端点)++的时候,一个更优的ed一定要大于或者等于原来的ed

所以尺取法是一种高效的枚举区间的方法,一般用于求取有一定限制的区间个数或最短的区间等等。

而且经常与map、set、multiset等连用

这里推荐几道题:

1、poj3061

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
    int n,S;
    int t;
    scanf("%d",&t);
    int a[100005];
    while(t--)
    {
        scanf("%d %d",&n,&S);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
         //printf("%d\n",sum[i+1]);
        }

        int res=n+1;
        int tr=0,s=0,sum=0;
      while1)
       {
           while(tr<n&&sum<S)
           {
               sum+=a[tr++];
           }
           if(sum<S)break;
          res=min(res,tr-s);
          sum-=a[s++];
       }
       if(res>n)res=0;
       printf("%d\n",res);
    }
}
poj3061

 

2、Codeforces Round #333 (Div. 2)

链接:http://codeforces.com/contest/602/problem/B

代码:

这道题结合了multiset

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define mod 1000000007
#define MAX 100005
int a[MAX];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    multiset<int>s;
    s.clear();
    s.insert(a[1]);
    int Max=a[1],Min=a[1];
    int st=1,ed=1;
    int ans=0;
    for(int i=2;i<=n;i++)
    {
        ed++;
        s.insert(a[i]);
        Max=max(Max,a[i]);
        Min=min(Min,a[i]);
        if(Max-Min<=1)
        ans=max((int)s.size(),ans);
        while(Max-Min>1&&s.size()>0)
        {
            multiset<int>::iterator it;
            it=s.find(a[st]);
            s.erase(it);
            st++;
            multiset<int>::iterator it1=--s.end();
            multiset<int>::iterator it2=s.begin();
            Max=*it1;
            Min=*it2;
        }
    }
    cout<<ans<<endl;
    return 0;
}
尺取法
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define mod 1000000007
#define INF 0x3f3f3f3f
#define MAX 100005
int Max[MAX],Min[MAX];
int n;
int a[MAX];
int lowbit(int x)
{
    return x&-x;
}
void updata(int i,int val)
{
    while(i<=n){
    Max[i]=max(Max[i],val);
    Min[i]=min(Min[i],val);
    i+=lowbit(i);
    }
}
bool query(int l,int r)
{
    int maxn=a[l],minn=a[r];
    while(1)
    {
        maxn=max(maxn,a[r]),minn=min(minn,a[r]);
        if(l==r) break;
        for(r-=1;r-l>=lowbit(r);r-=lowbit(r))
            maxn=max(Max[r],maxn),minn=min(minn,Min[r]);
    }
    return maxn-minn<=1;
}
bool C(int l,int r)
{
    return query(l,r);
}
int main()
{
    memset(Min,INF,sizeof(Min));
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
      updata(i,a[i]);
   }
   //cout<<query(1,2)<<endl;
    int ans=0;
   for (int i = 1; i <= n; i ++)
    {
        int l = i, r = n;
        while (l <= r)
        {
            int mid = (l + r)/2;
            if (C(i,mid)) l = mid+1;
            else r = mid-1;
        }
        //cout<<l<<r<<ans<<endl;
        ans = max(ans, r - i + 1);
   }
   cout<<ans<<endl;
    return 0;
}
树状数组+二分

3、poj3320

 

posted @ 2018-08-31 23:50  better46  阅读(201)  评论(0编辑  收藏  举报