1044 Shopping in Mars

博客围绕数字序列子串和问题展开,给定数字序列与数M,需找子串和等于M的左右端点,若不存在则找大于且最接近M的。通过令sum[i]表示序列前i项和,将问题转化为有序序列中二分查找首个大于等于特定值的问题,还介绍了lower_bound()函数用于求解。

题意:给出一个数字序列以及一个数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]  54687
sum[i]3561115212936
i012345678

考虑到可能不存在子序列之和恰为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;
}

 

转载于:https://www.cnblogs.com/kkmjy/p/9547957.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值