原题链接
解题思路
因为必须将num数组中一段连续的序列之和拿来付钱,而 [i, j] 之间元素之和显然可以通过sum[j] - sum[i-1]得到,而sum数组是递增的,因此可以通过枚举左端点,二分数组寻找右端点的方法来求解。
源代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int sum[maxn];//sum[i]存放num[0]~num[i]的累加结果
int lowerbound(int l, int r, int x){//在sum[l, r]中寻找第一个大于等于x的元素,并返回其下标
while(l<r){//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;
scanf("%d%d",&n,&m);
int x;
sum[0] = 0;
for(int i=1; i<=n; i++){
scanf("%d",&x);
sum[i] = sum[i-1] + x;
}
// for(int i=0; i<=n; i++){
// printf("%d ",sum[i]);
// }
// printf("\n");
int num,j,res = maxn;
bool flag = false;//是否有刚好价格的
for(int i=0; i<n; i++){
num = sum[i] + m;
//j = lower_bound(sum+i, sum+n+1, num)-sum;//系统函数
j = lowerbound(i, n+1, num);//自己写的
if(j<=n){
res = sum[j] - sum[i];
if(res==m){
printf("%d-%d\n",i+1,j);
flag = true;
}
res = min(sum[j]-sum[i],res);
}
}
if(!flag){
for(int i=0; i<n; i++){
num = sum[i]+res;
j = lowerbound(i, n+1, num);
if(j<=n){
if(sum[j] - sum[i] == res){
printf("%d-%d\n",i+1,j);
}
}
}
}
return 0;
}
注意
我是通过两次遍历的,第一次找出连续序列之和res大于等于m(需要给的钱)的最小值,如果等于m,直接输出左右端点,结束。
如果res!=m,那么需要二次遍历,这回输出连续序列之和等于res的左右端点。