YTU 3736 和为给定数 尺取法,二分法

问题 3736: 和为给定数

时间限制: 1.00s | 内存限制: 128MB

题目描述

给出若干个整数,询问其中是否有一对数的和等于给定的数。

输入

共三行: 第一行是整数 n (0<n≤100000),表示有 n 个整数。第二行是 n 个整数。整数的范围是在 0 到 10^8 之间。第三行是一个整数 m (0≤m≤2^30),表示需要得到的和。

输出

若存在和为 m 的数对,输出两个整数,小的在前,大的在后,中间用单个空格隔开。若有多个数对满足条件,选择数对中较小的数更小的。若找不到符合要求的数对,输出一行 No。(每个整数最多使用一次)(可能会出现相同的整数)

输入输出样例

样例输入 #1

复制

4
2 5 1 4
6
样例输出 #1

复制

1 5

算法:

这道题可以使用尺取法和二分法两种做法。其实这道题最容易出错的点是,一个数只能用一次,只要把握住这个点,其实问题不大

尺取法:

思路:

首先,对数组从小到大排序,然后设置两个变量i 和 j ,分别指向头和尾,i的初值为0,j的初值的n-1,然后让i 和 j 逐渐向中间移动,检查a[i]+a[j],如果大于m ,就让j 减1 ,如果小于m ,就让i加1 ,直至a[i]+a[j]=m.排序的复杂度为O(nlogn) ,检查的复杂度为O(n),总复杂度为O(nlogn)


代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int main()
{
    int n;
    cin>>n;
    for(int i=0; i<n; i++) cin>>a[i];
    sort(a,a+n);
    int m;
    cin>>m;
    int i=0;
    int j=n-1;
    while(i<j)
    {
        if(a[i]+a[j]<m)
        {
            i++;
        }
        if(a[i]+a[j]>m)
        {
            j--;
        }
        if(a[i]+a[j]==m && i!=j)  //注意i和j不能相同因为一个整数只能使用一次
        {
            cout<<a[i]<<" "<<a[j];
            return 0;
        }
    }
    cout<<"No";
    return 0;
}

二分法:

思路:

这道题二分的思路是首先对数组进行排序,复杂度为O(nlogn)然后从头到尾处理数组中的每一个元素a[i],在a[i]后面的数中二分查找是否存在一个等于m-a[i]的数,这一步的时间复杂度也是O(nlogn)。两部分相加,算法的时间复杂度还是O(nlogn)


代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N];
int main()
{
    int n; cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+1+n);
    int m; cin>>m;
    for(int i=1;i<=n;i++)
    {
        int l=i+1,r=n;  // 特别注意左边界是i+1 因为题干描述每个整数最多使用一次
        while(l<r)
        {
            int mid=(l+r)/2;
            if(a[mid]>= m-a[i]) r=mid;
            else l=mid+1;
        }
        if(a[l]!=m-a[i]) continue;
        else
        {
            cout<<a[i]<<" "<<m-a[i];
            return 0;
        }
    }
    cout<<"No";
    return 0;
}

注释:

nlogn 指的是n * 以2为底n的对数,因为数学公式不会打,希望谅解。
思路取自罗勇军老师的《算法竞赛》,罗老师博客:https://blog.csdn.net/weixin_43914593

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值