问题:给你一些数,请在这些数中找到一个区间,使得区间里每一个元素的和大于或等于给定的某个值。
题目很简单并不难懂,很容易想到双重循环,枚举区间起点和终点,然后每一次都求一次和,再和给定的数作比较,效率低下。
尺取法与它的模拟思路类似,都是寻找一个区间的起点和终点,但是一遍过高效的多。
尺取法算法过程思路:
用两个指针,最初都指向,这一组数中的第一个,然后如果这个区间的元素之和小于给定的数,就把右指针向右移(加数),直到区间和大于等于给定的值为止。然后开始更新答案取得最小值!把左指针向右移(减数),直到区间和等于给定的值为止,保存方案,继续操作。
假如左指针指向这些数的第一个,并且右指针指向这组数的最后一个,这种情况下的子区间元素之和仍然小于给定的数的话,表示这个序列不可能大于等于某值s。
本质也是一种模拟。用于解决:
1.区间和问题
2.求最小区间长度(满足。。条件的)
模板例题poj3061
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn=1005; 7 int a[maxn]; 8 9 int main() 10 { 11 ios::sync_with_stdio(false); cin.tie(0); 12 13 int T; 14 cin>>T; 15 while(T--) 16 { 17 int n,s,ans=1e6+5; 18 cin>>n>>s; 19 for(int i=1;i<=n;i++) 20 { 21 cin>>a[i]; 22 } 23 24 int sum=0,l=1,r=1; 25 while(r<=n) 26 { 27 while(sum<s && r<=n) 28 { 29 sum+=a[r]; 30 r++;//r可能大于n,r总是指向当前的下一个 31 } 32 33 while(sum>=s) 34 { 35 ans=min(ans,r-l);//因为指向了下一个所以不用再+1 36 sum-=a[l]; 37 l++; 38 } 39 } 40 if(l==1 && r>n) 41 { 42 cout<<'0'<<endl; 43 } 44 else 45 { 46 cout<<ans<<endl; 47 } 48 } 49 50 return 0; 51 }
分析和思路:
求最小的满足条件的区间长度,一个数记录区间的开始---一个数记录末尾, 先移动末尾--满足条件后移动开始(缩小区间)。
每次读入的时候就让 r 右移,如果最左边的出现过 l 右移,如果得到了num=26就可以更新答案。
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <map> 5 #include <cstdio> 6 #include <cstring> 7 using namespace std; 8 const int maxn=1e6+5; 9 char s[maxn]; 10 int vis[maxn]; 11 12 int main() 13 { 14 ios::sync_with_stdio(false); cin.tie(0); 15 16 cin>>s; 17 18 int len=strlen(s),num=0,l=0,r=0,ans=1e6+5; 19 while(r<=len-1) 20 { 21 while(num<26 && r<=len-1)//不满足条件一直右移 22 { 23 if(a[s[r]-'a']==0) 24 { 25 num++; 26 } 27 vis[s[r]-'a']++; 28 r++; 29 } 30 while(num>=26)//满足一直更新答案,直到不满足寻找下一个 31 { 32 ans=min(ans,r-l); 33 vis[s[l]-'a']--; 34 if(vis[s[l]-'a']==0) num--; 35 l++; 36 } 37 } 38 39 cout<<ans<<endl; 40 41 42 return 0; 43 }