二分查找

题目描述

总时间限制: 15000ms 单个测试点时间限制: 5000ms 内存限制: 228000kB
描述
The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .
输入
The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 228 ) that belong respectively to A, B, C and D .
输出
For each input file, your program has to write the number quadruplets whose sum is zero.
样例输入
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45
样例输出
5
提示
Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

解题分析

保存前两列所有组合的和a[],保存后两列的所有组合的和b[],对b数组排序,对与a[]中每个数,在b中二分查找其相反数。
注意在二分查找过程,需要在b中找到小于等于目标元素的最大值,如果这个最大值等于目标元素,再沿着数组向后找到相等的元素,计数即可。
这里采用的查找区间用闭区间表示,当target <= b[mid],right = mid,当target > b[mid],left = mid + 1,这中区间判定策略保证了b[left]<= target <= b[right],并且当target == b[mid]的时候,能将right左移到第一个等于target的b数组元素。退出循环的条件是 left == right,此时,查找区间就只有一个元素这个元素要么等于target,要么小于target,那么它就是小于等于target的最大元素,从这个元素开始向后找就能找到所有和target相等的元素。
通常我们使用的二分查找采用退出循环条件是left > right,因为在left == right,查找区间还有一个元素,但是不能判定这个元素一定就和目标元素相等,所以还需要一轮循环判定这个元素是否与目标元素相等。

解题代码

#include <cstdio>
#include <algorithm>
using namespace std;

int a[4010], b[4010], c[4010], d[4010];
int A[16000000], B[16000000];
int main(){
    int n;
    int num = 0;
    while(scanf("%d", &n) != EOF){
        num = 0;
        for(int i = 0; i < n; i++){
            scanf("%d%d%d%d", a+i, b+i, c+i, d+i);
        }
        int m = 0;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                A[m] = a[i] + b[j];
                B[m] = c[i] + d[j];
                m++;
            }
        }
        
        sort(B, B+m);
        
        for(int i = 0; i < m; i++){
            int target = -A[i];
            int l = 0, r = m - 1;
            int mid = l;
            while(l < r){
                mid = l + (r - l)/2;
                if(target <= B[mid])
                    r = mid;
                else
                    l = mid + 1;
            }
            while(B[l] == target && l < m){
                num++;
                l++;
            }
        }
        printf("%d
", num);
        
    }
}
原文地址:https://www.cnblogs.com/zhangyue123/p/12744323.html