[POJ3061]Subsequence(二分,前缀和)
题目链接:http://poj.org/problem?id=3061
题意:给一个长为n的数列和整数s,求一个连续的子序列,使得这个子序列长度最短并且不小于这个整数s。
统计[1~i]的子序列和sum(i),(sum(0)=0)。然后求一个区间[i,j]的和即为sum(j)-sum(i-1) (i > 0)。
由于给定序列没有负数,因此sum是个严格不减的序列。
转换成一个求最大值最小的问题,可以二分枚举序列长度,在前缀和上计算子序列[i-1,i+m-1]的和。如果存在一个满足子序列和≥s的,则缩小序列长度并记下当前值,反之扩大。复杂度为O(nlgn)。
1 #include <algorithm> 2 #include <iostream> 3 #include <iomanip> 4 #include <cstring> 5 #include <climits> 6 #include <complex> 7 #include <fstream> 8 #include <cassert> 9 #include <cstdio> 10 #include <bitset> 11 #include <vector> 12 #include <deque> 13 #include <queue> 14 #include <stack> 15 #include <ctime> 16 #include <set> 17 #include <map> 18 #include <cmath> 19 20 using namespace std; 21 22 const int maxn = 100010; 23 int n, s; 24 int x[maxn]; 25 int sum[maxn]; 26 27 bool ok(int mm) { 28 for(int i = 1; i <= n - mm + 1; i++) { 29 // printf("%d %d\n", i-1, i+mm-1); 30 if(sum[i+mm-1] - sum[i-1] >= s) return 1; 31 } 32 return 0; 33 } 34 35 int main() { 36 // freopen("in", "r", stdin); 37 int T; 38 scanf("%d", &T); 39 while(T--) { 40 memset(sum, 0, sizeof(sum)); 41 scanf("%d %d", &n, &s); 42 for(int i = 1; i <= n; i++) { 43 scanf("%d", &x[i]); 44 sum[i] = sum[i-1] + x[i]; 45 } 46 if(sum[n] < s) { 47 printf("0\n"); 48 continue; 49 } 50 int ans; 51 int ll = 0; 52 int rr = n; 53 while(ll <= rr) { 54 int mm = (ll + rr) >> 1; 55 if(ok(mm)) { 56 ans = mm; 57 rr = mm - 1; 58 } 59 else ll = mm + 1; 60 } 61 printf("%d\n", ans); 62 } 63 }
此题也可以用尺取法,维护两个指针从左到右扫描所存序列,复杂度为O(n)。
1 #include <algorithm> 2 #include <iostream> 3 #include <iomanip> 4 #include <cstring> 5 #include <climits> 6 #include <complex> 7 #include <fstream> 8 #include <cassert> 9 #include <cstdio> 10 #include <bitset> 11 #include <vector> 12 #include <deque> 13 #include <queue> 14 #include <stack> 15 #include <ctime> 16 #include <set> 17 #include <map> 18 #include <cmath> 19 20 using namespace std; 21 22 const int maxn = 100010; 23 int n, s; 24 int x[maxn]; 25 26 int main() { 27 // freopen("in", "r", stdin); 28 int T; 29 scanf("%d", &T); 30 while(T--) { 31 scanf("%d %d", &n, &s); 32 int sum = 0; 33 for(int i = 1; i <= n; i++) { 34 scanf("%d", &x[i]); 35 sum += x[i]; 36 } 37 if(sum < s) { 38 printf("0\n"); 39 continue; 40 } 41 int ans = 0x7f7f7f; 42 int ll = 1; 43 int rr = 1; 44 sum = 0; 45 while(1) { 46 while(rr <= n && sum <= s) { 47 sum += x[rr++]; 48 } 49 if(sum < s) break; 50 ans = min(ans, rr-ll); 51 sum -= x[ll++]; 52 } 53 printf("%d\n", ans); 54 } 55 }