UVA1121, 尺取法,二分,暴力
U V A − 1121 UVA - 1121 UVA−1121
给定一个长度为N的整数序列以及整数S。求最短的连续子序列的长度使得这个连续子序列的和大于等于S。
如果找不着,输出0。
暴力
暴力枚举所有的区间,求和,判断, O ( n 4 ) O(n^4) O(n4),哈哈
二分
先预处理前缀和 s u m i sum_i sumi表示前 i i i个数的和,然后去枚举区间的左端点,此时发现愈往右和越大,所以满足单调性,可以用二分找到满足条件的最小的右端点,更新答案,时间复杂度 O ( n log n ) ~O(n\log n)~ O(nlogn)
小茗同学曰:这还行,我写个代码吧~
#include <iostream>
#include <cstdio>
using namespace std;
const int inf = int (1E9);
const int N = 100010;
int a[N];
int sum[N];
int main()
{
int n, s;
while(scanf("%d%d", &n, &s) == 2)
{
for(int i = 1; i <= n;i ++)
{
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
int ret = inf;
// 枚举左端点
for(int i = 1; i <= n; i ++)
{
// 找一个满足条件的最小的右端点。
int l = i, r = n, best = 0;
while(l <= r)
{
int mid = (l + r) / 2;
if(sum[mid] - sum[i - 1] >= s)
{
best = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
if(best != 0)
{
if(best - i + 1 < ret)
{
ret = best - i + 1;
}
}
}
if(ret == inf)
{
ret = 0;
}
printf("%d\n", ret);
}
return 0;
}
尺取法
维护两个指针 l=1和 r=1,while循环一遍,如果总和达不到 ,右端点 指向下一个元素,否则左端点 指向下一个元素,因为有两个指针,所以也戏称“双指针法”
小茗同学曰:这方法太妙了! O ( N ) ! O(N)! O(N)!
#include <iostream>
#include <cstdio>
using namespace std;
int a[100010];
int main()
{
int n, m;
while(scanf("%d%d", &n, &m) == 2)
{
for(int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
}
int ret = 100010001;
int j = 0;
int tmp = 0;
for(int i = 1; i <= n; i ++)
{
while(j + 1 <= n && tmp < m)
{
tmp += a[j + 1];
j ++;
}
if(tmp >= m)
{
if(j - i <= ret)
{
ret = j - i;
}
}
tmp -= a[i];
}
if(ret == 100010001)
{
ret = -1;
}
ret ++;
printf("%d\n", ret);
ret = 0;
}
return 0;
}