https://www.cnblogs.com/violet-acmer/p/9793209.html
马上就要去上课了,先献上二分AC代码,其余的有空再补
题意:
给定长度为 n 的整数数列 a[0,1,2,........,n]以及整数 S。
求出总和不小于 S 的连续子序列的长度的最小值。
如果解不存在,则输出 0。
题解:
1、二分
由于所有的元素都大于 0 ,所以数组a[ ] 的前缀和sum[ ]为递增的序列,满足二分的条件。
首先确定子序列的起点为start(start的可能取值为 1,2,......,n)。
假设区间[start,end]是以start为子序列起点的最小区间,则需要满足 sum[end]-sum[start-1] >= S,而确定满足条件的最小的 end 用二分即可在O(longn)的时间完成。
所以总的时间复杂度为 O(nlogn)
2、尺取法
相关说明:
设以a[start]为子序列起点的总和最初大于S的连续子序列为a[start,......,end],此时 res = end-start+1;
(1):end++,找到最大的 k ,使得在去除当前子序列的前 k 个数后依旧满足 sum[end]-sum[start-1 + k] >= S,并判断是否需要更新 res。
(2):重复(2)过程,直到 end > n 为止。
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define mem(a,b) (memset(a,b,sizeof(a))) 6 const int maxn=1e5+50; 7 8 int N,S; 9 int a[maxn]; 10 int sum[maxn]; 11 int binarySearch(int val) 12 { 13 int l=0,r=N+1; 14 while(r-l > 1) 15 { 16 int mid=l+((r-l)>>1); 17 if(sum[mid] >= val) 18 r=mid; 19 else 20 l=mid; 21 } 22 return r; 23 } 24 int main() 25 { 26 int T; 27 scanf("%d",&T); 28 while(T--) 29 { 30 mem(sum,0); 31 scanf("%d%d",&N,&S); 32 int res=0; 33 for(int i=1;i <= N;++i) 34 { 35 scanf("%d",a+i); 36 sum[i] += sum[i-1]+a[i]; 37 } 38 for(int s=1;s <= N;++s) 39 { 40 int t=binarySearch(sum[s-1]+S); 41 if(t != N+1) 42 res=(res==0 || (t-s+1)<res ? t-s+1:res); 43 } 44 printf("%d\n",res); 45 } 46 }