[ACM_其他] 总和不小于S的连续子序列的长度的最小值——尺缩法
Description:
给定长度为n的整数数列,A[0],A[1],A[2]….A[n-1]以及整数S,求出总和不小于S的连续子序列的长度的最小值。如果解不存在,则输出0。
Input:
输入数据有多组,每组数据第一行输入n,S, (10<n<10^5,S<10^8)第二行输入A[0],A[1],A[2]….A[n-1] ( 0<A[i]≤10000),处理到文件结束。所有输入数据都在int范围之内。
Output:
每组数据输出结果占一行。
Sample Input:
10 15 5 1 3 5 10 7 4 9 2 8
Sample Output:
2
>>>>>>>>>这题有一个技巧,叫做尺缩法
-----------------------------------------------------------------------------------------------------------------------
(1) 设置两个指针s和t,一开始都指向数列第一个元素,此外sum=0,res=0;
(2) 只要sum<S,就将sum增加一个元素,t加1;
(3) 直到sum>=S,更新res=min(res,t-s);
(4) 将sum减去一个元素,s加1,执行(2)。
上述流程反复地推进区间的开头和末尾,来求取满足条件的最小区间。
----------------------------------------------------------------------------------------------------------------------------------------------------------
#include<iostream> using namespace std; int min(int a,int b){if(a>b)return b;return a;} int main(){ int n,S,A[100005]; int i,j,k; while(cin>>n>>S){ //输入数据部分 for(int i=0;i<n;i++)cin>>A[i]; //尺缩法计算部分 int res=n+1,s=0,t=0,sum=0; for(;;){ while(t<n && sum<S)sum+=A[t++]; if(sum<S)break; res=min(res,t-s); sum-=A[s++]; } if(res>n)res=0; //输出结果 cout<<res<<'\n'; } return 0; }
>>>>>>>>> 同样也可以用用lower_bound:
--------------------------------------------------------------------------------------------------------------------------------------------------------
lower_bound(ForwardIterator first,ForwardIterator last, const Type &value,Compare comp );
lower_bound()返回一个 iterator 它指向在[first,last)标记的有序序列中可以插入value,而不会破坏容器顺序的第一个位置,而这个位置标记了一个大于等于value 的值。
例如,有如下序列:
我们可以计算出sum(i)=a0+a1+...+ai。那么sum(t)-sum(s)=as+a(s+1)+...a(t-1)。这样我们可以实现先求出一个sum(n)。sum(n)-sum(i)=s。我们只需要去筛选sum(i)。这样可以用二分搜索的方法快速求出最小的长度。
int t=lower_bound(sum+i,sum+n,sum[i]+s)-sum;求出来的是从ai到at(i<=t<=n)和比s小的最小值的下标。
1 #include<algorithm> 2 using namespace std; 3 int min(int a,int b){if(a>b)return b;return a;} 4 int n,s; 5 int a[100005]; 6 int sum[100005]; 7 int main(){ 8 while(cin>>n>>s){ 9 //sum归0; 10 memset(sum,0,sizeof(sum)); 11 //输入并计算sum; 12 for(int i=0;i<n;i++){ 13 cin>>a[i]; 14 sum[i+1]=sum[i]+a[i]; 15 } 16 //运算求解输出; 17 if(sum[n]<s){ 18 cout<<"0"<<endl; 19 } 20 else{ 21 int ret=n; 22 for(int i=0;sum[i]+s<=sum[n];i++){ 23 int t=lower_bound(sum+i,sum+n,sum[i]+s)-sum; 24 ret=min(ret,t-i); 25 } 26 cout<<ret<<endl; 27 } 28 } 29 return 0; 30 }
>>>>>>>>附加:折半查找法:
1 int lowerBound(int array[],int left,int right,int value) 2 { 3 int mid=0,half=0; 4 int len=(right+left+1)/2; 5 while(len>0) 6 { 7 half=len>>1; //数据长度折半 8 mid=left+half; //计算中点 9 if (array[mid]<value) //调整总长与起点 10 { 11 len=len-half-1; 12 left=mid+1; 13 } 14 else 15 len=half; 16 } 17 return left; 18 }
1 int lowerBound(int array[],int left,int right,int value) 2 { 3 int mid=0; 4 while(left<right) 5 { 6 mid=(right+left)/2; //计算中点 7 if (array[mid]<value) //调整起点或者终点 8 left=mid+1; 9 else 10 right=mid; 11 } 12 return right; 13 }
1 int upperBound(int array[],int left,int right,int value) 2 { 3 int mid=0,half=0; 4 int len=(right+left+1)/2; 5 while(len>0) 6 { 7 half=len>>1; //长度折半 8 mid=left+half; //计算中点 9 if (array[mid]>value) //调整长度与起点 10 len=half; 11 else 12 { 13 len=len-half-1; 14 left=mid+1; 15 } 16 } 17 return left; 18 }
1 int upperBound(int array[],int left,int right,int value) 2 { 3 int mid=0; 4 while(left<right) 5 { 6 mid=(right+left)/2; //计算中点 7 if (array[mid]>value) //调整起点或者终点 8 right=mid; 9 else 10 left=mid+1; 11 } 12 return right; 13 }
1 int binarySearch(int array[],int left,int right,int value) 2 { 3 int mid; 4 while(left<=right) 5 { 6 mid=(left&right)+((left^right)>>1); //防止溢出 7 if(array[mid]==value) 8 return mid; 9 else if (array[mid]<value) 10 left=mid+1; 11 else 12 right=mid-1; 13 } 14 return -1; 15 }