尺取法 poj3061

来源:互联网 发布:linux全局翻墙教程 编辑:程序博客网 时间:2024/05/17 03:25

尺取法:就是两个指针表示区间[l,r]的开始与结束然后根据题目来将端点移动,是一种十分有效的做法。适合连续区间的问题

1 poj3061

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

方法1;暴力:O(n^3枚举)

方法2:通过预先计算sum[i],那么子序列的和就可以在O(1)内找到

注意这题是positive integer,那么sum肯定是递增的,对于每个位置,通过二分搜索找到S-sum[i]<sum[k]的最小k,O(nlogn)

方法3:尺取法:我们若有a[s]+...a[t-1]>S

分析:我们知道连续子序列的和可以在输入的时候获取sum[i],那么i+1到j的连续序列和就可以用sum[j]-sum[i]获得了

主要思想为:当a1,  a2  , a3 满足和>=S,得到一个区间长度3,那么去掉开头a1,   剩下 a2,a3,判断是否满足>=S,如果满足,那么区间长度更新,如果不满足,那么尾部向后拓展,判断a2,a3,a4是否满足条件。重复这样的操作。
个人对尺取法的理解:当一个区间满足条件时,那么去掉区间开头第一个数,得到新区间,判断新区间是否满足条件,如果不满足条件,那么区间末尾向后扩展,直到满足条件为之,这样就得到了许多满足条件的区间,再根据题意要求什么,就可以在这些区间中进行选择,比如区间最长,区间最短什么的。这样跑一遍下来,时间复杂度为O(n)。

<span style="font-size: 14px;">#include <iostream>#include <cstdio>#include <iostream>using namespace std;const int maxn=100005;int a[maxn];int sum[maxn];int main(int argc, char const *argv[]){int t,n,s,left,right;cin>>t;while(t--){sum[0]=0;cin>>n>>s;int ans=n+1;for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}for(left=0,right=1;left<n;left++){while(right<=n&&sum[right]-sum[left]<s){right++;}//the sum of left+1 to rightif(right==n+1) break;if(right-left<ans) ans=right-left;}if(ans==n+1) cout<<"0"<<endl;else cout<<ans<<endl;}return 0;}</span><span style="font-size: 16px;"></span>



0 0
原创粉丝点击