题意:给出一个数字序列以及一个数M,要求在这个数字序列中找到子串(子串是连续的),使得子串的数字之和等于M,输出该子串对应的左右端点(左端点小的先输出,如果左端点相同,则右端点小的先输出);如果无法找到子串之和恰等于M的,则找出子串之和大于M且最接近M的。注意,题中的下标是从1开始的。
思路:令sum[i]表示序列a[1],a[2],...,a[i]之和(为了表示方便,初始化sum[0]=0),这样一来,要求连续子序列a[i]~a[j]之和,等价于求sum[j]-sum[i-1]。观察下表:
a[i] | 3 | 2 | 1 | 5 | 4 | 6 | 8 | 7 | |
sum[i] | 0 | 3 | 5 | 6 | 11 | 15 | 21 | 29 | 36 |
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
考虑到可能不存在子序列之和恰为M的情况,因此,本题实际上是求a[i]+...+a[j]>=M,即sum[j]-sum[i-1]>=M,因此遍历sum[],对于指定的i,找到第一个满足sum[j]-sum[i-1]>=M的j即可。而由于题目中所说,数字序列的值均为正数,因此sum[]是一个严格单调递增的序列,问题就转化为——在一个有序序列中,对于一个确定的x,求第一个大于等于x的值(这是一个经典的二分查找问题)。在这里,有序序列就是sum[],x就是sum[i-1]+M,sum[j]就是我们要找的。下面给大家介绍一个好用的函数 lower_bound() 。
ForwardIt lower_bound( ForwardIt first, ForwardIt last, const T& value ); 函数查找区间[first,last)内首个大于等于value的元素,对于容器,如果存在返回指向该元素的迭代器,如果不存在,则返回尾迭代器last。注,要求区间[first,last)内的元素有序。回到这个题目,用上这个函数很快就能解决了,即语句1。然后更新,判定sum[j]-sum[i-1]是否是恰好等于M还是大于M就好了。
代码:
#include <cstdio> #include <vector> #include <algorithm> using namespace std; //存放答案 struct Node{ int i,j; }; vector<Node> ans; int minlost=0x7fffffff;//最小损失 int main() { //freopen("pat.txt","r",stdin); int n,amount; scanf("%d%d",&n,&amount); vector<int> sum(n+1); 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++){ auto it=lower_bound(sum.begin()+i,sum.end(),sum[i-1]+amount);//语句1 int j=it-sum.begin();//j即迭代器it对应的下标 //printf("[%d %d]\n",i,j); Node node; node.i=i; node.j=j; //以下为更新过程 if(sum[j]-sum[i-1]==amount){ if(minlost!=0){ ans.clear(); ans.push_back(node); minlost=0; }else{ ans.push_back(node); } }else if(sum[j]-sum[i-1]>amount && sum[j]-sum[i-1]-amount<minlost){ minlost=sum[j]-sum[i-1]-amount; ans.clear(); ans.push_back(node); }else if(sum[j]-sum[i-1]>amount && sum[j]-sum[i-1]-amount==minlost){ ans.push_back(node); } } for(auto it:ans) printf("%d-%d\n",it.i,it.j); return 0; }
下面这个稍有区别,即sum[]数组是用C的数组,而不是vector,也能用lower_bound()函数,但是不推荐。
#include <cstdio> #include <vector> #include <algorithm> using namespace std; const int N=100005; int sum[N]={0}; //存放答案 struct Node{ int i,j; }; vector<Node> ans; int minlost=0x7fffffff;//最小损失 int main() { //freopen("pat.txt","r",stdin); int n,amount; scanf("%d%d",&n,&amount); for(int i=1;i<=n;i++){ scanf("%d",&sum[i]); sum[i]+=sum[i-1]; } for(int i=1;i<=n;i++){ int j=lower_bound(sum+i,sum+n,sum[i-1]+amount)-sum;//关键 //printf("[%d %d]\n",i,j); Node node; node.i=i; node.j=j; //以下为更新过程 if(sum[j]-sum[i-1]==amount){ if(minlost!=0){ ans.clear(); ans.push_back(node); minlost=0; }else{ ans.push_back(node); } }else if(sum[j]-sum[i-1]>amount && sum[j]-sum[i-1]-amount<minlost){ minlost=sum[j]-sum[i-1]-amount; ans.clear(); ans.push_back(node); }else if(sum[j]-sum[i-1]>amount && sum[j]-sum[i-1]-amount==minlost){ ans.push_back(node); } } for(auto it:ans) printf("%d-%d\n",it.i,it.j); return 0; }