Subsequence(POJ 3061)
- 原题如下:
Subsequence
Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 21032 Accepted: 8975 Description
A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.Input
The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.Output
For each the case the program has to print the result on separate line of the output file.if no answer, print 0.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
- 题解:由于所有的元素大于0,假设存在子序列[s,t)满足as+…+at-1≥S,那么对于任何的t'>t一定有as+…+at'-1≥S。另外,如果令sum(i)=a0+a1+…+ai-1,那么as+as+1…+at-1=sum(t)-sum(s),所以预先计算好sum的话,可以用O(1)的时间计算区间上的总和,这样一来,子序列的起点s确定以后,便可以用二分搜索快速确定使序列和不小于S的结尾t的最小值,这个算法的复杂度是O(nlogn)。还有一种更为高效的算法,设以as开始总和最初大于S时的连续子序列为as+…+at-1,这时,as+1+…+at-2<as+…+at-2<S,所以从as+1开始总和最初超过S的连续子序列如果是as+1+…+at'-1的话,必然有t'≥t。利用这一性质,可设计如下算法:
①以s=t=sum=0初始化
②只要依然有sum<S,就不断将sum增加at,并将t增加1
③如果②中无法满足sum≥S则终止,否则,更新res=min(res, t-s)
④将sum减去as,s增加1然后回到②
对于这个算法,因为s,t只能不断地增大,最多变化n次,因此只需O(n)的复杂度就可以求解这个问题了,像这样反复地推进区间的开头和结尾,来求取满足条件的最小区间的方法被称为尺取法。 - 代码1:
1 #include <cstdio> 2 #include <cctype> 3 #include <algorithm> 4 #include <cmath> 5 #define num s-'0' 6 7 using namespace std; 8 9 const int MAX_N=1000000; 10 const int INF=0x3f3f3f3f; 11 int n,S,t; 12 int a[MAX_N],sum[MAX_N+1]; 13 14 int min(int x, int y) 15 { 16 if (x<y) return x; 17 return y; 18 } 19 20 void read(int &x){ 21 char s; 22 x=0; 23 bool flag=0; 24 while(!isdigit(s=getchar())) 25 (s=='-')&&(flag=true); 26 for(x=num;isdigit(s=getchar());x=x*10+num); 27 (flag)&&(x=-x); 28 } 29 30 void write(int x) 31 { 32 if(x<0) 33 { 34 putchar('-'); 35 x=-x; 36 } 37 if(x>9) 38 write(x/10); 39 putchar(x%10+'0'); 40 } 41 42 int main() 43 { 44 read(t); 45 while (t>0) 46 { 47 read(n);read(S); 48 for (int i=0; i<n; i++) 49 { 50 read(a[i]); 51 sum[i+1]=sum[i]+a[i]; 52 } 53 int res=n+1; 54 for (int s=0; sum[n]-sum[s]>=S; s++) 55 { 56 int t=lower_bound(sum+s,sum+n,sum[s]+S)-sum; 57 res=min(res,t-s); 58 } 59 if (res<=n) write(res); 60 else write(0); 61 putchar('\n'); 62 --t; 63 } 64 }
代码2:
1 #include <cstdio> 2 #include <cctype> 3 #include <algorithm> 4 #include <cmath> 5 #define num s-'0' 6 7 using namespace std; 8 9 const int MAX_N=1000000; 10 const int INF=0x3f3f3f3f; 11 int n,S,m; 12 int a[MAX_N]; 13 14 int min(int x, int y) 15 { 16 if (x<y) return x; 17 return y; 18 } 19 20 void read(int &x){ 21 char s; 22 x=0; 23 bool flag=0; 24 while(!isdigit(s=getchar())) 25 (s=='-')&&(flag=true); 26 for(x=num;isdigit(s=getchar());x=x*10+num); 27 (flag)&&(x=-x); 28 } 29 30 void write(int x) 31 { 32 if(x<0) 33 { 34 putchar('-'); 35 x=-x; 36 } 37 if(x>9) 38 write(x/10); 39 putchar(x%10+'0'); 40 } 41 42 int main() 43 { 44 read(m); 45 while (m>0) 46 { 47 read(n);read(S); 48 for (int i=0; i<n; i++) 49 { 50 read(a[i]); 51 } 52 int s,t,sum; 53 s=t=sum=0; 54 int res=n+1; 55 for (;;) 56 { 57 while (t<n && sum<S) sum+=a[t++]; 58 if (sum<S) break; 59 res = min(res,t-s); 60 sum-=a[s++]; 61 } 62 if (res>n) res=0; 63 write(res); 64 putchar('\n'); 65 --m; 66 } 67 }