其他分享
首页 > 其他分享> > 尺取法

尺取法

作者:互联网

目录

尺取法

1. 算法分析

尺取法: 尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。尺取法通常可以把一个O(n^2^)的算法利用特殊性质优化到O(n)的复杂度。
for example:
给定一个数列a[n]和一个S, 要求找出连续的一段区间,使得区间和大于等于S,打印这个区间长度的最小值。

尺取流程
n = 5, S = 11
a[i]: 1 2 3 4 5
一开始:l = 1, r = 1, sum = 0
然后r不断右移:r = 5, sum = 15
右移l:l = 2, sum=15-1=14>=S
继续左移l直到:l = 3, sum = 12
此时找到最小区间长度为2。

2. 板子

2.1 一维尺取

#include <iostream>
#include <cstdio>

using namespace std;

int const N = 1e5 + 10, INF = 1e9 + 10;
int a[N], n, S, T;

int main() {
    cin >> T;
    while (T--) {
        cin >> n >> S;
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        
        int l = 1, r = 1, sum = 0, res = INF;  // 初始化左、右边界、区间和
        while (1) {
            while (r <= n && sum < S) sum += a[r++];  // 计算区间和
            if (sum < S) break;  // 如果小于S说明找不到>=S的情况,直接break
            res = min(res, r - l);  // 更新区间
            sum -= a[l++];  // 右移l
        }
        if (res == INF) res = 0;
        cout << res << endl;
    }
    return 0;
}

3. 例题

Poj3061 Subsequence
题意: 给定一个数列a[n]和一个S, 要求找出连续的一段区间,使得区间和大于等于S,打印这个区间长度的最小值。
题解: 尺取模板题,具体见代码,思路见1.1
代码:

#include <iostream>
#include <cstdio>

using namespace std;

int const N = 1e5 + 10, INF = 1e9 + 10;
int a[N], n, S, T;

int main() {
    cin >> T;
    while (T--) {
        cin >> n >> S;
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        
        int l = 1, r = 1, sum = 0, res = INF;  // 初始化左、右边界、区间和
        while (1) {
            while (r <= n && sum < S) sum += a[r++];  // 计算区间和
            if (sum < S) break;  // 如果小于S说明找不到>=S的情况,直接break
            res = min(res, r - l);  // 更新区间
            sum -= a[l++];  // 右移l
        }
        if (res == INF) res = 0;
        cout << res << endl;
    }
    return 0;
}

poj3320Jessica's Reading Problem
题意: 给定一个n,然后给定n个数字(可能重复),要求取得最小的连续区间,使得能够取到这n个数字中所有的数字。n <= 10^6^
题解: 和上题类似,先while循环判断是否已经取到所有出现的数字,然后收缩左端点。尺取法处理即可。

#include <iostream>
#include <cstdio>
#include <set>
#include <map>

using namespace std;

int const N = 1e6 + 10, INF = 1e9 + 10;
set<int> s;
int a[N], n;
map<int, int> cnt;

int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        s.insert(a[i]);
    }
    int l = 1, r = 1, res = INF, sum = 0, S = s.size();  // S为需要达到的数目,sum记录当前总的数目
    while (1) {
        while (r <= n && sum < S) {
            if (cnt[a[r++]]++ == 0) sum++;  // 只有cnt中没出现过才能使得sum++
        }
        if (sum < S) break;
        res = min(res, r - l);  // 更新
        if (--cnt[a[l++]] == 0) sum--;  // l右移使得cnt[a[l]]为时才需要sum--
    }
    cout << res << endl;
return 0;
}

标签:10,int,res,sum,取法,区间,include
来源: https://www.cnblogs.com/spciay/p/13546588.html