尺取法(取尺法)及模板題 poj-3061 Subsequence

什麼是尺取法(取尺法)

        其實我也不太懂到底是尺取法還是取尺法,好像都可以= =下文統一尺取法。其實很多人寫的雙指針就就是尺取法,只是突然換個名字,大概的思路以及應用都是類似的,雙指針是2個指針移動,尺取法就是設定尺子的兩端位置,然後移動。

簡單的例子

        假設給出一串01組成的字符串,要求找出連續的0或者連續的1的最長長度,比較簡單的寫法就是雙指針,定義兩個指針,指針範圍內右指針先一直右移,直到碰見不一樣的字符,此時用變量記錄兩個指針間的距離(由於指針是一下一下移動的,所以每次變量自加1即可)。然後左指針直接移動到右指針所在位置,然後繼續重複。比較每次結束的時候的距離,更新一個最大值即可。

        尺取法基本一樣,這裏上一下代碼:

int l,r;
int ans = 0;
for(l = 0,r = 0;l < n-1;l++){
	int sum = 0;
	while(r+1 < n && arr[r+1] == arr[l]){
		r++;
		sum++;
	}
	ans = ans > sum ? ans : sum;
}

基本也比較簡單,大概就是用l、r定義區間,先固定l爲初始,然後r一直累加,直到發現不同的字符出現。然後l++,也就是左邊少一位,然後r繼續移動。

        實際上對於這個問題而言,雙指針是要優於尺取法的,因爲中間連續的部分完全可以跳過。那麼如果改一下問題呢?

        給出一段數字,再給出一個數字k,要求子串的和大於這個k的所有子串,最短長度是多少。

        那麼雙指針就gg了,因爲左指針跳到右指針的時候就錯過了很多子串的開頭。但是我們如果還是想用雙指針的話,就直接變成了尺取法了。我們維護一個區間,每次右邊移動一位,同時累加到sum,當sum>k的時候,就計算當前長度。雙指針仍然需要計數,但是尺取法就不用了,r-l即可得到長度。然後左邊移動一位,同時減去這個sum。就這樣,當sum<=k的時候就一直右移,當sum>k的時候左移一下,然後重複。

尺取法使用的前提

        上面也看到了,尺取法的使用需要一定的前提才能發揮最大的作用。第一個問題雙指針就可以了,但是第二個就需要尺取法來維護一個區間。

1.能夠維護一個區間,保證這個區間能夠獲得答案。

2.維護的具體操作可以左邊移動一位、右邊移動一位。

3.區間的變化是連續的而不是跳躍的。問題1就是比較跳躍的,雙指針比較好,問題2不能跳躍。

4.必須符合題意:答案越小越好。

當然,4感覺不太必要,如果答案越大越好就沒有意義了。

尺取法的一個模板題-poj 3061-Subsequence

poj 3061 Subsequence

        題意其實就是上面的問題2了,就不再贅述,直接上代碼:

#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#include<math.h>
#include<malloc.h>
#include<string.h>
#include<stack>
#include<functional>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MOD 1000000007
#define MAX 101000
#define INF 0x3f3f3f3f

using namespace std;

int T, N, S;
LL arr[MAX];

LL Min(LL a, LL b) {
    return a > b ? b : a;
}

int main() {
    TIME;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &N, &S);
        for (int i = 0; i < N; i++) {
            scanf("%lld", &arr[i]);
        }
        LL ans = INF;
        LL sum = arr[0];
        int l = 0;
        int r = 0;
        if (N == 0) {
            printf("1\n");
        }else {
            for (; l < N; l++) {
                while (r + 1 < N && sum < S) {
                    r++;
                    sum += arr[r];
                }
                if (sum >= S) {
                    ans = Min(ans, r - l+1);
                }
                sum -= arr[l];
            }
            if (ans == INF) {
                printf("0\n");
            }else {
                printf("%lld\n", ans);
            }
        }
    }

    system("pause");
    return 0;
}

這個題其實有一點很蛋疼,得考慮0的情況,否則會WA的。其他還好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章