PAT.A1044 Shopping in Mars_子序列

题意

给出一个数字序列与一个数m,在数字序列中求出所有和值为S的连续子序列(区间下标左端点小的先输出,左端点相同时右端点小的先输出)。若没有这样的序列,求出和值恰好大于S的子序列(即在所有和值大于S的子序列中和值最接近S)。假设序列下标从1开始。

样例(可复制)

16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13

样例输出

1-5
4-6
7-8
11-11

提供几个易错的测试数据

3 3
1 2 3
//output
1-2
3-3
3 5
2 2 2
//output
1-3
1 10
18
//output
1-1

注意点

  1. 本题可以使用二分法和two pointers解决,时间复杂度为O(nlog n)
  2. lower_bound()函数会返回查找范围内第一个大于等于指定值的数,而之前的upper_bound()函数会返回查找范围内第一个大于指定值的数;也可以不使用这两个自带的函数,自己写个二分查找函数

二分法

#include <bits/stdc++.h>
using namespace std;

int n,m,small=INT_MAX;
int main(){
cin>>n>>m;
int sum[n];//存储一组数中第1个数到第i个数的和
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&sum[i]);
sum[i]+=sum[i-1];
}
for(int i=1;i<=n;i++){//找到small
int j=lower_bound(sum+i,sum+n,sum[i-1]+m)-sum;
if(sum[j]-sum[i-1]==m){
small=m;break;
}else if(sum[j]-sum[i-1]>m&&sum[j]-sum[i-1]<small)
small=sum[j]-sum[i-1];
}
for(int i=1;i<=n;i++){//输出
int j=lower_bound(sum+i,sum+n,sum[i-1]+m)-sum;
if(sum[j]-sum[i-1]==small)printf("%d-%d\n",i,j);
}
return 0;
}

two pointers

#include <bits/stdc++.h>
using namespace std;

int n,m,small=INT_MAX;
int main(){
cin>>n>>m;
int sum[n];//存储一组数中第1个数到第i个数的和
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&sum[i]);
sum[i]+=sum[i-1];
}
int j=1;
for(int i=0;i<=n;i++){//找到small
while(j<=n){
if(sum[j]-sum[i]>=m){
if(small>sum[j]-sum[i])small=sum[j]-sum[i];
break;
}
j++;
}
}
j=1;
for(int i=0;i<=n;i++){//输出
while(j<=n){
if(sum[j]-sum[i]>small)break;
if(sum[j]-sum[i]==small){
printf("%d-%d\n",i+1,j);
break;
}
j++;
}
}
return 0;
}