尺取法/双指针法(更新于2021.6.13)

//瞎写的

尺取法:通过保存一组下标,根据条件交替挪动以求最终解。

一般用于求取有一定限制的区间个数或最短的区间,有时能把一些枚举问题从O(nlogn)(二分)优化到O(n)。

例题:

Subsequence POJ3061

题意:给定长度为n的都是正整数的数列以及整数S。求出总和不小于S的连续子序列的长度的最小值,不存在则输出0。

思路:先记录下标b1=1,找到最小的下标b2使得b1->b2的子序列总和不小于S(不存在则输出0)并记录长度。然后将b1增加一,重复前一个过程寻找新的最小b2并计算长度,与原长度比较记录最小值。

分析,在重复过程中我们不难发现,(b1+1)->b2序列的总和必定小于S,故我们没有必要重新枚举(b1+2)->b2的点,可以保证新的下标b2大于原先的b2。

思路代码表现:

void solve()
{
    int l=1,r=n,ans=n+1,sum=0;
    while(1)
    {
        while(r<=n&&sum<S)sum+=a[r++];
        if(sum<S)break;
        ans=min(ans,r-l);//r在递归时会到最小情况+1的位置
        sum-=a[l++];
    }
    if(ans>n)ans=0;
    cout<<ans<<endl;
}

Jessica's Reading Problem POJ3320

题意:给定长度为n的数列,数列值可能有重复。求出现过所有元素值的子序列的最小长度。

思路:其实相同,只是判断的条件变成了所有元素是否都出现过。

思路代码表现:

void solve()
{
    int l=1,r=n,cnt=0,ans=n+1;
    map<int,int>mp;
    while(1)
    {
        while(r<=n&&sum<n)
        {
            if(!mp[a[r++]])
            {
                mp[a[r++]]++;
                cnt++;
            }
        }
        if(cnt<n)break;
        ans=min(ans,r-l);
        if(mp[a[l++]]==1)
        {
            mp[a[l++]]--;
            cnt--;
        }
    }
    cout<<ans<<endl;
}

//水了一篇

 

//更新于2021.6.13

CF1538C. Number of Pairs

题意:给定n个元素和区间[l,r],求元素组成的数列中存在多少个两个数字的组合使得 l<=ai+aj<=r。

思路:排序过后易发现对于取组合中的一个数,另一个数的最大下标递减。

代码思路:

int main()
{
    cin>>t;
    while(t--)
    {
        ll l,r,cnt=0;
        cin>>n>>l>>r;
        rep(i,0,n-1)cin>>a[i];
        sort(a,a+n);
        ll L=n,R=n;
        rep(i,0,n-1)
        {
            while(L&&a[i]+a[L-1]>=l)L--;
            while(R&&a[i]+a[R-1]> r)R--;
            cnt+=min(i,R)-min(i,L);
        }
        cout<<cnt<<endl;
    }
}

 

posted @ 2021-06-10 20:00  Geospiza  阅读(120)  评论(0)    收藏  举报