PAT-A 1044. Shopping in Mars (25)

来源:互联网 发布:模具编程软件 编辑:程序博客网 时间:2024/06/17 05:30

题目链接在此。

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

题意

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