九度 1534:数组中第K小的数字(二分法变形)

题目描述:

给定两个整型数组A和B。我们将A和B中的元素两两相加可以得到数组C。
譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6]。
现在给你数组A和B,求由A和B两两相加得到的数组C中,第K小的数字。

 

思路

1. 多年前第一次搞这道题就没弄出来, 今天是大概记得做法, 不过 failed again, 总结起来, 仍然是没有意识到 int 的特殊性, 即 (int+int)/2 得到的仍然是 int

2. 涉及到二分查找, 用我以前总结的步骤来做, 非常顺利. 核心就是 mid = target, high = mid-1, 尝试向左. 最后返回的应该是 low

3. target 没有明确给出, 是通过 ini 的特殊性拼凑出来的

4. 曾想将两个数组拼成矩阵, 因为 Leetcode 和剑指 offer 上都有类似的矩阵二分查找题目, 但失败了

 

代码

#include <iostream>
#include <stdio.h>
#include <algorithm>

using namespace std;

long long a[100010];
long long b[100010];

long long search(long long x, long long m, long long n) {
    long long cnt = 0;
    long long j = n-1;
    for(long long i = 0; i < m && j >= 0; i++) {
        if(a[i] + b[j] <= x) {
            cnt += (j+1);
        }else{
            while(j >= 0 && a[i]+b[j] > x) {
                j--;
            }
            if(j >= 0)
                cnt += (j+1);
        }
    }
    //cout << "cnt = " << cnt << endl;
    return cnt;
}

long long doCal(long long m, long long n, long long k) {
    long long low = a[0]+b[0];
    long long high = a[m-1] + b[n-1];

    while(low <= high) {
        long long mid = (low+high)>>1;
        long long cnt = search(mid, m, n);
        if(cnt >= k) {
            high = mid -1;
        }else{
            low = mid + 1;
        }
    }
    return low;
}

int main() {
    long long m, n, k;
    //freopen("testcase.txt", "r", stdin);
    while(scanf("%lld%lld%lld", &m, &n, &k) != EOF) {
        for(long long i = 0; i < m; i ++)
            scanf("%lld", &a[i]);
        for(long long i = 0; i < n; i ++)
            scanf("%lld", &b[i]);
        sort(a, a+m);
        sort(b, b+n);

        long long res = doCal(m, n, k);
        printf("%lld\n", res);

    }

    return 0;
}

 

你可能感兴趣的:(二分法)