问题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805439202443264
题意:
给出一个数字序列与一个数m,在数字序列中求出所有和值为m的连续子序列(区间下标左端点小的先输出,左端点相同时右端点小的先输出)。若没有这样的序列,求出和值恰好大于m的子序列(即所有和值大于m的子序列中和值最接近m)。假设序列下标从1开始。假设序列下标从1开始。
思路:
令sum[i]表示A[1] ~A[i]的和值,则sum数组单调递增。连续子序列A[i] ~A[j]的和等于sum[j] - sum[i-1]。因此用二分法寻找sum数组中值大于sum[i-1] + m的元素下标(有sum[j] - sum[i-1] = m)推得,并按要求将合适的数保存在nearm中。并再次循环找sum[j-1] - sum[i-1] = nearm的数组下标。
#include<cstdio>
#include<algorithm>
using namespace std;
int n, m, nearm = 100000010;
int sum[100010];
int main(){
scanf("%d %d",&n, &m);
sum[0] = 0;
for(int i = 1; i <= n; i++){
scanf("%d",&sum[i]);
sum[i] += sum[i-1];
}
//第一个循环,判断是否有和值为 m 的子序列
//若有,nearm = m; 若没有,求和值恰好大于m的子序列,和值保存在nearm中
for(int i = 1; i <= n; i++){
//在[i,n+1)中找第一个大于sum[i - 1] + m的位置
int j = upper_bound(sum + i, sum + n + 1, sum[i - 1] + m) - sum;
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];
}
for(int i = 1; i <= n; i++){
//在[i,n+1)中找第一个大于sum[i - 1] + nearm的位置
int j = upper_bound(sum + i, sum + n + 1, sum[i - 1] + nearm) - sum;
if(sum[j - 1] - sum[i - 1] == nearm)
printf("%d-%d\n", i, j - 1);
}
return 0;
}