题目 A1044 Shopping in Mars
-
题意
输出序列和最接近M
的所有序列区间,区间从1
开始。 -
思路
求序列和的方法一般都采用前缀和的方式去解决。这里前缀和的数组sum
单调递增,抽象为二分查找其实就是要找sum[i-1] + m
的下标,找不到的时候需要返回大于该值的第一个。所以二分查找right
更新方式为right = mid
。因为找不到的时候需要给出序列和最小的,需要遍历一遍之后才能知道所以这里需要两步,一步去更新nearm
然后第二步输出序列区间和等于nearm
的区间。 -
Code in C++
#include <cstdio>
#define maxn 100001
long long sum[maxn] = {0};
// 返回第一个大于x的下标
int upper_bound(int L, int R, int x)
{
while (L < R)
{
int mid = L + (R-L)/2;
if (sum[mid] > x)
{
R = mid;
}
else
{
L = mid + 1;
}
}
return L;
}
int main()
{
int n, m, nearm = 100000010;
scanf("%d %d", &n, &m);
// 边输入边获取前缀和
for (int i = 1; i <= n; ++i)
{
scanf("%d", &sum[i]);
sum[i] += sum[i-1];
}
// 获取nearm
for (int i = 1; i <= n; ++i)
{
int j = upper_bound(i, n + 1, sum[i-1] + m);
if (sum[j-1] - sum[i-1] == m)
{
nearm = m;
break;
}
else if (j <= n && sum[j] - sum[i-1] < nearm)
{
nearm = sum[j] - sum[i-1];
}
}
// 输出所有序列和为nearm的段
for (int i = 1; i <= n; ++i)
{
int j = upper_bound(i, n + 1, sum[i-1] + nearm);
if (sum[j-1] - sum[i-1] == nearm)
printf("%d-%d\n", i, j-1);
}
return 0;
}
小结
二分查找 + 区间和 = 前缀和方式