1. 程式人生 > 其它 >尺取法/雙指標法

尺取法/雙指標法

//瞎寫的

尺取法:通過儲存一組下標,根據條件交替挪動以求最終解。

一般用於求取有一定限制的區間個數或最短的區間,有時能把一些列舉問題從O(nlogn)(二分)優化到O(n)。

例題:

SubsequencePOJ3061

題意:給定長度為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; }

//水了一篇