zoukankan      html  css  js  c++  java
  • 尺取法

    反复推进区间的开头与末尾,这样的方法叫做尺取法,求给定长度内的最短区间可以满足某些性质。


    POJ3061

    题意:

    给定长度为 n 的数列整数,以及整数 S ,求出总和不小于 S 的连续子序列的长度的最小值.如果解不存在则输出 0 。

    解:

    不断的推进首位,每推进一次首位,就往后推近末尾直到区间序列的和大于等于S。

     1 int N, S;
     2 int a[MAXN];
     3 
     4 void solve() {
     5     int res = N + 1;
     6     int s = 1, t = 1, sum = 0;
     7     for (;;) {
     8         while (t <= N && sum < S) {
     9             sum += a[t++];
    10         }
    11         if (sum < S) break;
    12         res = min(t - s, res);
    13         sum -= a[s++];
    14     }
    15     if (res > N) res = 0;
    16     printf("%d
    ", res);
    17 }
    18 
    19 int main() {
    20 #ifndef ONLINE_JUDGE
    21     freopen("input.txt", "r", stdin);
    22 #endif
    23     int T = READ();
    24     while (T--) {
    25         CLR(a);
    26         scanf("%d %d", &N, &S);
    27         REP(i, 1, N) scanf("%d", &a[i]);
    28         solve();
    29     }
    30     return 0;
    31 }

    POJ3320

    题意:

    一本书总共有 P 页,第 i 页有一个知识点 $a_i$ (每个知识点都有一个编号),求最少读连续的多少页就可以掌握所有知识点

    解法:

    尺取法,用一个单独的变量维护当前读过的区间内的知识数,判断其与知识总数是否相等,若相等则更新答案,否则继续推。

     1 int P;
     2 int a[MAXN];
     3 set<int> all;
     4 
     5 void solve() {
     6     int num = all.size();
     7     int s = 1, t = 1, sum = 0;
     8     int res = P;
     9     map<int, int> count;
    10     for (;;) {
    11         while (t <= P && sum < num) {
    12             if (count[a[t++]]++ == 0) sum++;
    13         }
    14         if (sum < num) break;
    15         res = min(t - s, res);
    16         if (--count[a[s++]] == 0) sum--;
    17     }
    18     printf("%d
    ", res);
    19     return;
    20 }
    21 
    22 int main() {
    23 #ifndef ONLINE_JUDGE
    24     freopen("input.txt", "r", stdin);
    25 #endif
    26     scanf("%d", &P);
    27     for (int i = 1; i <= P; i++) {
    28         scanf("%d", &a[i]);
    29         all.insert(a[i]);
    30     }
    31     solve();
    32     return 0;
    33 }

    POJ2566

    题意:

    从数列中找出连续序列,使得和的绝对值与目标数之差最小。

    多组目标数,数列长度为1e6

    解法:

    注意是要求和的绝对值,那么我们就可以先把前缀和都算出来,然后用尺取法一直推进即可。

    因为是前缀和,所以不存在两个区间没有交集的情况。

    代码如下:

     1 int n, m, k;
     2 pair<LL, int> sum[MAXN];
     3 
     4 LL myabs(LL x) { return x >= 0 ? x : -x; }
     5 
     6 void solve() {
     7     while (m--) {
     8         int s = 0, t = 1;
     9         LL ans = LLINF, th, tmpans = LLINF;
    10         int l, r;
    11         scanf("%lld", &th);
    12 
    13         while (t <= n) {
    14             LL diff = sum[t].first - sum[s].first;
    15             if (myabs(diff - th) < tmpans) {
    16                 tmpans = myabs(diff - th);
    17                 ans = diff;
    18                 l = sum[s].second;
    19                 r = sum[t].second;
    20             }
    21             if (diff == th) {
    22                 break;
    23             } else {
    24                 diff > th ? s++ : t++;
    25             }
    26             if (s == t) t++;
    27         }
    28         if (l > r) swap(l, r);
    29         printf("%lld %d %d
    ", ans, l + 1, r);
    30     }
    31     return;
    32 }
    33 
    34 int main() {
    35 #ifndef ONLINE_JUDGE
    36     freopen("input.txt", "r", stdin);
    37 #endif
    38     while (scanf("%d %d", &n, &m), n + m) {
    39         sum[0].first = 0;
    40         sum[0].second = 0;
    41         for (int i = 1; i <= n; i++) {
    42             LL x;
    43             scanf("%lld", &x);
    44             sum[i].first = sum[i - 1].first + x;
    45             sum[i].second = i;
    46         }
    47         sort(sum, sum + n + 1);
    48         solve();
    49     }
    50     return 0;
    51 }

    POJ2739

    题意:给定一个数,求这个数可以分解为多少个连续素数的和

    解法:

    素数打标之后直接尺取法即可,注意这个表的下表是从1开始的

    代码如下:

     1 bool visit[10100000];
     2 int prime[10000000];
     3 int T;
     4 
     5 int prime_num = 0;
     6 void generate_prim(int n) {
     7     memset(visit, true, sizeof(visit));
     8     for (int i = 2; i <= n; ++i) {
     9         if (visit[i] == true) prime[++prime_num] = i;
    10         for (int j = 1; ((j <= prime_num) && (i * prime[j] <= n)); ++j) {
    11             visit[i * prime[j]] = false;
    12             if (i % prime[j] == 0) break;
    13         }
    14     }
    15     return;
    16 }
    17 
    18 int main() {
    19 #ifndef ONLINE_JUDGE
    20     freopen("input.txt", "r", stdin);
    21 #endif
    22     generate_prim(10000 + 1);
    23     while (scanf("%d", &T) && T) {
    24         int cnt = 0;
    25         int s = 1, t = 1, sum = 0;
    26         for (;;) {
    27             while (t <= prime_num && sum < T) {
    28                 sum += prime[t++];
    29             }
    30             if (sum < T) break;
    31             if (sum == T) cnt++;
    32             sum -= prime[s++];
    33             if (prime[s] > T) break;
    34         }
    35         printf("%d
    ", cnt);
    36     }
    37     return 0;
    38 }

    POJ2100

    题意:给定一个数,求这个数T( T < 1e14 )可以分解为多少个连续数的平方和,输出方案数与每个方案

    解法:T开根号之后正好1e7,O( n )就可以过,所以这里用尺取法即可

     1 LL T;
     2 vector<vector<LL>> ans;
     3 
     4 LL poww(LL num) { return num * num; }
     5 
     6 bool cmp(const vector<LL>& v1, const vector<LL>& v2) {
     7     return v1.size() > v2.size();
     8 }
     9 
    10 int main() {
    11     while (cin >> T) {
    12         // T = 2030;
    13         LL s = 1, t = 1;
    14         LL sum = 0;
    15         LL len = (int)sqrt(T * 1.0) + 1;
    16         ans.clear();
    17         for (;;) {
    18             while (poww(t) <= T && sum < T) {
    19                 sum += poww(t++);
    20             }
    21             if (sum < T) break;
    22             if (sum == T) {
    23                 vector<LL> v;
    24                 for (int i = s; i < t; i++) {
    25                     v.push_back(i);
    26                 }
    27                 ans.push_back(v);
    28             }
    29             sum -= poww(s++);
    30             if (poww(s) > T) break;
    31         }
    32 
    33         sort(ans.begin(), ans.end(), cmp);
    34         printf("%d
    ", ans.size());
    35         for (int i = 0; i < ans.size(); i++) {
    36             printf("%d", ans[i].size());
    37             // sort(ans[i].begin(), ans[i].end());
    38             for (int j = 0; j < ans[i].size(); j++) {
    39                 printf(" %lld", ans[i][j]);
    40             }
    41             printf("
    ");
    42         }
    43     }
    44     return 0;
    45 }
  • 相关阅读:
    SQLite的SQL语法
    C/C++中各种类型int、long、double、char表示范围(最大最小值)
    君子性非异也,善假于物也
    简单工厂模式
    Linux下通配符总结
    Readprocessmemory使用方法
    C++ 清空消息队列
    一周自学动态站点设计
    iOS 8.0正式公布啦
    What is the difference between JRE,JVM and JDK?
  • 原文地址:https://www.cnblogs.com/romaLzhih/p/11619882.html
Copyright © 2011-2022 走看看