尺取法,顧名思義,像尺子一樣,一塊一塊的截取。
是不是感覺從字面很難理解?
我們直接看例題:poj-3061
題目翻譯:
給定長度爲N的數列整數a0,a1,a2,a3 ….. an-1以及整數S。求出綜合不小於S的連續子序列的長度的最小值。如果解不存在,則輸出0。
限制條件:
10< N<10^5, S<10^8, a[]<1000這裏我們拿第一組測試數據舉例子,即
n=10, S = 15, a = {5,1,3,5,10,7,4,9,2,8}
這幅圖便是尺取法怎麼“取”的過程了。
整個過程分爲4步:
1.初始化左右端點
2.不斷擴大右端點,直到滿足條件
3.如果第二步中無法滿足條件,則終止,否則更新結果
4.將左端點擴大1,然後回到第二步
用尺取法來優化,使複雜度降爲了O(n)。
最後,再給一個尺取法的定義以便更好理解:返回的推進區間開頭和結尾,求滿足條件的最小區間的方法稱爲尺取法。
其實,個人覺得這種方法稱之爲蚯蚓法更好,像蚯蚓一般一點一點的先縮起來蓄勢再往前移動。
至於代碼,已經貼出來過,再貼一次吧
#include <iostream>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn=100010;
int num[maxn];
int n,S;
int main()
{
int t;scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&S);
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
int sum=0,head=1,p=1;
int ans=n+1;
for(;;)
{
while(p<=n&&sum<S)
sum+=num[p++];
if(sum<S)
break;
ans=min(ans,p-head);
sum-=num[head++];
}
if(ans==n+1)
cout<<0<<endl;
else
cout<<ans<<endl;
}
return 0;
}
分析
用sum累加sum數組找到第一個滿足sum>=S的sum[i]的 p 值,再依次縮小起始地址 head 到 p 的距離,獲得一個 p-head 的值然後再將 p 值後移重複上述操作。