尺取法(挑战程序设计竞赛)


尺取法

尺取法一般用于求子序列的和、乘积等,给遍历求权值以一个更低的时间复杂度的方法。

反复地推进区间的开头和结尾,来求取满足条件的最小区间的方法

例题1

题目描述

给出了N个正整数序列**(10 < N < 100,000),每个正整数小于或等于10000**,一个正整数S (S < 100 000 000)。编写一个程序,求序列中连续元素的子序列的最小长度,其和大于或等于S

输入描述

第一行是测试用例的数量。对于每个测试用例,程序都必须读取从第一行开始的数字NS,它们之间用间隔隔开。序列的编号在测试用例的第二行给出,用间隔隔开。输入将在文件结束时结束。

输出描述

对于每种情况,程序都必须在输出文件的单独行上打印结果。如果没有回答,打印0。

输入

2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5

输出

2
3

算法分析

1 2 3

解题代码

void solve()
{
  int res = n + 1;
  int s = 0, t = 0, sum = 0;
  
  for(;;){
    while(t < n && sum < S)
      sum += a[t++];	//尾加
    
    if(sum < S)	//如果0~n的sum都比S小,那就不存在解
      break;
    res = min(res, t - s);
    sum -= a[s++];	//头减
  }
  
  if(res > n)
    //解不存在
    res = 0;
  
  printf("%d\n", res);
}

例题2

题目描述

杰西卡是一个非常可爱的女孩,很多男孩都在追求她。最近她有个问题。期末考试就要到了,但她还没怎么花时间。如果她想通过考试,她必须掌握一本厚厚的教科书中包含的所有内容。那本教科书的作者,和其他作者一样,对知识点非常挑剔,因此有些知识点被多次提及。杰西卡认为如果她能把每个知识点至少读一遍,她就能通过考试。她决定只阅读书中一个连续的部分,其中包含了整本书涵盖的所有知识点。当然,子目录应该尽可能的薄。

一个非常勤奋的男孩为她手工索引了杰西卡课本的每一页,并告诉了她每一页的知识点,这对他的求爱来说是一个很大的进步。你是来拯救杰西卡的,请给出索引,帮助杰西卡决定她应该读哪个相邻的部分。为了方便起见,每个想法都被编码了一个ID,它是一个非负整数。

输入描述

输入的第一行是整数P(1 ≤ P ≤ 1000000),即Jessica的课本页数。第二行包含P个非负整数,描述每页的内容。第一个整数是第一页的内容,第二个整数是第二页的内容,以此类推。您可以假设出现的所有整数都能很好地适合有符号32位整数类型。

输出描述

输出一行:书中最短连续部分的页数,包含了书中所有的知识点。

输入

5
1 8 8 8 1

输出

2

算法分析

4

代码

#include <iostream>
#include <cstdio>
#include <map>
#include <set>
using namespace std;

int p;
int a[1000005];
map<int, int> ln;	//记录每个知识点出现了多少次,知识->出现次数的映射
set<int> all;	//统计知识点的总数
int main()
{
    cin >> p;
    
    int cnt = 0;
    for(int i = 0; i < p; i++){
        scanf("%d", &a[i]);
        all.insert(a[i]);
    }
    
    cnt = (int)all.size();
    
  	//尺取法
    int e = 0,s = 0;
    int ans = p;
    int res = 0;
    for(;;){
        while(e < p && res != cnt){
            if(ln[a[e]] == 0)
                res++;	//如果一个知识点新出现,那么总共学的知识点数增加
            ln[a[e]]++;
            e++;
        }
        if(res != cnt)
            break;
        ans = min(ans, e - s);
        ln[a[s]]--;	//头删除,将该知识点出现次数减1
        if(!ln[a[s]]) //如果知识点减为0,就说明这个知识点未出现
            res--;
        s++;
    }
  	//尺取法
    
    printf("%d\n", ans);
    
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

省下洗发水钱买书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值