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的数列(都为正整数) , 求出总和不超过S的连续子序列的长度的最小值,如果不存在则输出0 ;
写法:先递推出所有项的和, 确定出子序列的起点s,就可以二分查找到末端;
PS:这个题输入量巨大,就算用二分也会导致擦边而过,跑了860MS,当然还可以优化的,但是用了输入挂,跑了16MS。对于巨量的数据这个还是很好用 的。
AC代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std ; int a[100000000+5] , sum[100000000+5]; inline bool scan_d(int &num) { char in;bool IsN=false; in=getchar(); if(in==EOF) return false; while(in!='-'&&(in<'0'||in>'9')) in=getchar(); if(in=='-'){ IsN=true;num=0;} else num=in-'0'; while(in=getchar(),in>='0'&&in<='9'){ num*=10,num+=in-'0'; } if(IsN) num=-num; return true; } int main() { int t ; cin>>t; int n , S ; while(t--) { scan_d(n); scan_d(S); for(int i = 0 ; i<n ; i++) { scan_d(a[i]); } for(int i = 0 ; i < n ; i++) { sum[i+1] = sum[i] + a[i] ; } if(sum[n] < S) { printf("0\n"); continue ; } int res = n ; //*s为起点 for(int s = 0 ; sum[s] + S <=sum[n] ; s++) { int t = lower_bound(sum+s , sum + n , sum[s]+S) - sum ;//*折半求出后结点位置; res = min(res , t - s );//*比较前后结点距离, } cout << res <<endl ; } return 0 ; }
尺取法:
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> using namespace std; int a[100000000] , S, n ; int main() { int m ; cin>>m; while(m--) { cin>>n>>S; for(int i = 0 ; i < n ; i++) { cin>>a[i]; } int res = n+1 ; int s , ans , t ; s = ans = t = 0 ; //*ans代表和, s代表前节点,t代表后节点,res=t-s就是区间长度; for(;;) { while(t<n&&ans<S) { ans+=a[t++]; } if(ans < S) break; res = min(res , t -s); ans-=a[s++]; } if(res > n) printf("0\n"); else printf("%d\n",res); } return 0 ; }