题目链接在此。
这里在《算法笔记》中归类的二分查找当中,但是目前的题解不是二分法的思路。
题意
给出一个整数n和一个整数pay,然后给出n个整数,在这n个整数中找到所有连续的整数使得这些个连续的整数的和等于pay,如果没有这样的整数序列,则找到大于pay且最接近pay的这样的整数序列。下标从1开始。
思路
暴力枚举求解的方法是最容易想到的,即i从1开始枚举,然后j从1开始枚举。但是这种方法会有三个测试点超时。
后来看了《算法笔记》,里面用的二分的做法,虽然也比较好理解,但是我感觉这种做法虽然有三个测试点超时,但是在细节上修改一下应该能AC,后来在网上找了,也的确看到了相应的题解记录,比如这个。
所谓细节上的优化是指:
1. 输入数据的存储上,使用数组保存前i项的和,而不是第i项,即sum[i]=j表示从第1项到第i项的和为j,这样就省去了求前i项和的时间。
2. 当找到了一组序列和大于或等于pay的序列,马上退出j循环,因为下面找到的一定是比这组的序列和要大的序列,不需要继续找下去。
3. 当j循环已经到了n,还没找到符合的条件的序列,则可以跳出所有循环,输出已经保存在ans数组中的信息了。因为j到了n了还没找到符合条件的序列,i++之后的序列和只能更小,也是没有必要再找下去了。
以上就可以通过。不过输入输出需要用printf和scanf,用cin&cout貌似会有一个点超时。
AC代码
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int pay;
int sum[100010];
int index1 = 0; //控制ans数组的小标,不用index是因为和c++的关键字重复
int ans[100010][2]; //保存刚好大于pay的序列的下标上下界
int min = 100000010;
int main(){
scanf("%d %d",&n, &pay);
sum[0] = 0;
for(int i = 1; i <= n; i++){
scanf("%d",&sum[i]);
sum[i] += sum[i-1];
}
int i,j ;
bool flag = false; //用来标记是否有序列和等于pay的序列,有的话flage = true
for(i = 1; i <= n; i++) {
j = i;
while(j <= n){
int s = sum[j] - sum[i-1];
if( s >= pay){ //找到了符合条件的序列
if(s == pay){ //序列和等于pay
printf("%d-%d\n",i,j); //直接输出
flag = true; //设置标志位
}else{ //s > pay
if(s < min){ //如果s比当前序列和最小的那个序列还小
index1 = 0; //,则覆盖掉之前的
min = s;
ans[index1][0] = i;
ans[index1][1] = j;
index1++;
} else if(s == min){ // s和当前序列和最小的那个序列一样大
min = s;
ans[index1][0] = i; //则保存进ans数组
ans[index1][1] = j;
index1++;
}
}
break;
}
j++;
}
if(j > n) break;
}
if(flag == false){ //如果没有序列和等于pay的序列
for(int i = 0; i < index1; i++){ //则输出序列和刚好大于pay的序列
printf("%d-%d\n",ans[i][0],ans[i][1]);
}
}
return 0;
}
由于s==pay的情况会是s>=pay的情况中最小的那个,所以也可以将两种情况结合起来,写成如下这样。
#include<cstdio>
#include<cstring>
using namespace std;
int n;
int pay;
int sum[100010];
int index1 = 0; //控制ans数组的小标,不用index是因为和c++的关键字重复
int ans[100010][2]; //保存刚好大于pay的序列的下标上下界
int min = 100000010;
int main(){
scanf("%d %d",&n, &pay);
sum[0] = 0;
for(int i = 1; i <= n; i++){
scanf("%d",&sum[i]);
sum[i] += sum[i-1];
}
int i,j ;
for(i = 1; i <= n; i++) {
j = i;
while(j <= n){
int s = sum[j] - sum[i-1];
if( s >= pay){ //找到了符合条件的序列
if(s < min){ //如果s比当前序列和最小的那个序列还小
index1 = 0; //,则覆盖掉之前的
min = s;
ans[index1][0] = i;
ans[index1][1] = j;
index1++;
} else if(s == min){ // s和当前序列和最小的那个序列一样大
min = s;
ans[index1][0] = i; //则保存进ans数组
ans[index1][1] = j;
index1++;
}
break;
}
j++;
}
if(j > n) break;
}
for(int i = 0; i < index1; i++){ //则输出序列和刚好大于pay的序列
printf("%d-%d\n",ans[i][0],ans[i][1]);
}
return 0;
}