PAT-A 1044. Shopping in Mars (25)

题目链接在此

这里在《算法笔记》中归类的二分查找当中,但是目前的题解不是二分法的思路。

题意

给出一个整数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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值