尺取法:通过左右移动两个端点,遍历一个数列,从而求解一个.....的最短区间的算法,就是尺取法。(像毛毛虫一样 头部移动一段 尾部移动一段) 复杂度O(n)
题目特征:1.答案满足某个条件的最短连续序列(或区间)
2.任意两个合法区域(满足题目条件的区间)[a,b],[c,d]。当a>c时,必有d>=b
满足以上两个条件,首先考虑使用尺取法
例1 , POJ3061:Subsequence
Description
Input
Output
Sample Input
2 10 15 5 1 3 5 10 7 4 9 2 8 5 11 1 2 3 4 5
Sample Output
2 3
给长度为n的数组和一个整数m,求总和不小于m的连续子序列的最小长度
n = 10,m = 15
5 1 3 5 10 7 4 9 2 8
那么我们先用sum存当前这个子序列的和,从左边第一个数来存,直到这个子序列的和大于等于m为止,再记录下当前长度。
其实相当于当不满足条件就入队,然后得到队列长度,再将队首元素出队,再进行下一次的
入队,直到满足条件再次出队,并且将这一次的长度与历史最短长度进行取舍,最后扫到最
后的元素却无法再满足入队条件的时候就结束,此时用O(n)的时间就可以得到答案。
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
5 1 3 5 10 7 4 9 2 8
总结:1.区间和<S,右端右移
2. 区间和>S,尝试更新答案,左端右移
#include<iostream> #include<cstdio> #include <queue> using namespace std; int a[100005]; int main(){ int T; scanf("%d",&T); int n,m; while(T--) { scanf("%d%d",&n,&m); int ans = n+1; for(int i=0;i<n;i++) { scanf("%d",&a[i]); } int now = 0; queue<int> q; int flag = 0; for(int i=0;i<n;i++) { q.push(a[i]); now+=a[i]; if(now>m) { while(now>=m) { flag = 1; int t = q.size(); ans = min(ans,t); now-=q.front(); q.pop(); } } } if(!flag) printf("0\n"); else printf("%d\n",ans); } }
未完。。