Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 14836 | Accepted: 6272 |
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
Source
问题链接:POJ3061 ZOJ3123 Subsequence。
题意简述:求长度为n的序列的一个子序列的最小长度,满足该子序列和不小于S。
问题分析:
有两个方法可以解决该问题,一是前缀和+二分搜索,二是尺取法。
1.前缀和+二分搜索
计算前缀和是一个关键。有了前缀和,判断前缀和的差值是否大于s,就可以找到满足条件的最小子序列长度。
需要注意的是,计算前缀和时需要有prefixsum[0]=0这个元素。
二分搜索可以使用函数lower_bound()来实现。
2.尺取法
尺取法是先求出最前面的子序列,使之满足子序列和大于s;
重复后面一步,直到n个元素都使用到;
去掉子序列的第1个元素,子序列的后面再加上其他元素,满足子序列大于s的子序列,找出最小的长度。
这个问题和《UVALive2678 UVA1121 Subsequence【前缀和+二分搜索+尺取法】》应该说是同一个问题,只是输入格式略有不同而已。
使用前缀和+二分搜索的方法时,原来的元素是不必存储的,有前缀和数组就够了。
参考链接:UVALive2678 UVA1121 Subsequence【前缀和+二分搜索+尺取法】
AC的C++语言程序(前缀和+二分查找)如下:
/* POJ3061 ZOJ3123 Subsequence */
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100000;
int prefixsum[N+1];
int main()
{
int t, n, s, val, ans;
cin >> t;
while(t--) {
cin >> n >> s;
// 输入数据,计算前缀和
prefixsum[0] = 0;
for(int i=1; i<=n; i++) {
cin >> val;
prefixsum[i] = prefixsum[i - 1] + val;
}
if(prefixsum[n] < s)
ans = 0;
else {
ans = n;
for(int i=0; prefixsum[i] + s < prefixsum[n]; i++) {
int pos = lower_bound(prefixsum + i, prefixsum + n, prefixsum[i] + s) - prefixsum;
ans = min(ans, pos - i);
}
}
cout << ans << endl;
}
return 0;
}
AC的C++语言程序(尺取法)如下:
/* POJ3061 ZOJ3123 Subsequence */
#include <iostream>
using namespace std;
const int N = 100000;
int a[N+1];
int solve(int n, int s)
{
int res = n + 1;
int start=0, end=0, sum=0;
for(;;) {
while(end < n && sum < s)
sum += a[end++];
if(sum < s)
break;
res = min(res, end - start);
sum -= a[start++];
}
return (res > n) ? 0 : res;
}
int main()
{
int t, n, s;
cin >> t;
while(t--) {
cin >> n >> s;
for(int i=0; i<n; i++)
cin >> a[i];
cout << solve(n, s) << endl;
}
return 0;
}