POJ3579 Median(两次二分)

题目链接

Description

Given N numbers, X1, X2, … , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i < j ≤ N). We can get C(N,2) differences through this work, and now your task is to find the median of the differences as quickly as you can!
Note in this problem, the median is defined as the (m/2)-th smallest number if m,the amount of the differences, is even. For example, you have to find the third smallest one in the case of m = 6.

Input

The input consists of several test cases.
In each test case, N will be given in the first line. Then N numbers are given, representing X1, X2, … , XN, ( Xi ≤ 1,000,000,000 3 ≤ N ≤ 1,00,000 )

Output

For each test case, output the median in a separate line.

Sample Input

4
1 3 2 4
3
1 10 2

Sample Output

1
8

思路

首先按照正常二分思想,我们需要通过二分来获取差值的中位数,但在检验时不容易想到方法。对于这些数而言,中位数差值的下标是可以得知的,即为(C(n,2)/2+1)/2。也就是在所有情况中,需要有这么多数目的差值小于等于中位数。我们不妨设当前中位数为x,对于n个数中的任一个,若其他数大于当前数+x,就说明他们之间的差值大于x,我们利用upper_bound函数找到排序后第一个大于的下标并减1,即可获取差值小于等于x的数量,然后累加与正确值比较即可。值得注意的是,因为题目所要求为差值的绝对值,所以可能认为这种方法情况不全。但正负的情况实际上总对称出现,若我们在计数时统一取正,则由于相互之间都会取到的原因,从而确保总数正确。(若使用lower_bound需要记住它查找的是第一个小于等于的数,所以需要将数值加1)

代码

#include<map>
#include<stack>
#include<queue>
#include<string>
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
const int maxn=1e6+5;
typedef long long ll;
int k,m,n,a[maxn];
bool check(double x)
{
    int cnt=0;
    for(int i=1;i<n;i++)
        cnt+=upper_bound(a+1+i,a+1+n,a[i]+x)-a-i-1;//upper_bound找到首个大于a[i]+x的下标,减1后再减去当前下标即可获得差值绝对值小于等于当前的数量
    if(cnt>=m)
        return true;//当前下标大于答案,说明中位数取大了
    else
        return false;
}//差值中位数下标
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        m=(n*(n-1)/2+1)/2;
        int l=0,r=a[n]-a[1];
        while(l<r)
        {
            int mid=(l+r)/2;
            if(check(mid))
                r=mid;
            else
                l=mid+1;
        }
        printf("%d\n",l);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值