Median(两个二分,好难呀……)

题目链接:https://cn.vjudge.net/contest/279225#problem/L

题目大意:第一行输入n,第二行输入n个整数,所以这些整数互相之间的差值一共有C(n,2)个,这些差值升序排好序之后,要我们找到这些差值中的最中间的那个差值。如果差值的个数是偶数,那么就是最中间的两个数的左边那个。

思路:因为所给的范围很大,所以我们用二分查找那个中间的那个差值。首先是差值的范围,差值的范围就是[0,a[n-1]-a[0]](这里我们把n个数从小到大排好序)。

然后就二分开始。区间变化的条件就是mid是中间差值的话,有多少对元素满足这个条件,满足条件的sum小于k,就说明这个mid有点太大了,这个时候我们要取右边的区间;满足条件的sum大于等于k(中间差值是排好序后的差值序列的第k位),就说明这个mid可行(中间的那个差值不一定和左右的数不同),但是不一定是最终我们要找的中间值,所以我们现在就要把区间向左移,看有没有更合适的mid。

最后区间长度为1时,[min,max]假设mid(min)可行,也就是我们要往左边取了,也就是[min,min-1],这个时候,肯定min和min-1都是大于等于k的,但是显然后者的sun更大,所以前者min是最后最优答案。如果mid显然不行,那么就是往右取,也就是[min+1,min],这个时候左区间端点是答案,还是min。

然后我们怎么找这个sum呢?首先呢就是说假设一个升序的序列,a[i]-a[j]<=x,那么任意mm属于[j,i-1]都满足a[i]-a[mm]<=x.知道这个之后呢,我们就利用这个来计算sum。从第一个数前边没有数了,所以不存在差值,所以我们从第二个数开始看,也就是我们的jjudge函数,我们的区间这个时候后就是[0,xx-1]这个很显然。然后我们就看a[xx]和前边的哪个元素的差值是第一个小于等于mid的,这个时候我们又一次用到了二分。

如果a[xx]-a[mmid]的差值大于mid,说明我们mmid找小了,所以区间右移,如果小于等于mid,就说明我们的这个mmid可以,但是不一定是最合适的,因为如果小于的话就显然是mmid大了所以需要向左移。

最后区间长度为0时,[left,right],判断a[xx]-a[mmid(left)]是否大于mid,如果大于mid,那么就是mmid小了,往右移,也就是[left+1,left];否则往左移,也就是[left,left-1];显然就是取左端点为我们要找的答案。

最后sum加的是i-jjudge(i),因为我们jjudge返回的是第一个和a[i]的差值小于等于mid的元素的下标,所以满足条件的区间长度是[jjudge(i),i-1],区间长度是i-jjudge(i).
然后就完了,over!

这道题用long long超时了!!!!!噗 我也不知道为啥……反正就改了int之后就A了……
可能是因为longlong比较长,然后调用慢……我也不知道,呜呜呜呜呜呜,一天!就改这道题了……

#include <stdio.h>
#include <math.h>
#include <algorithm>
#define INF 100005
using namespace std;
typedef long long ll;
int N,k,min_,max_,mid;
int a[INF];
int jjudge(int xx)
{
    int left=0,right=xx-1,mmid;
    while(left<=right)
    {
        mmid=(left+right)/2;
        if(a[xx]-a[mmid]>mid)
            left=mmid+1;
        else
            right=mmid-1;
    }
    return left;
}
bool judge()
{
    int sum=0;
    for(int i=1;i<N;i++)
    {
        sum+=i-jjudge(i);
    }
    return sum<k;
}
int main()
{
    while(~scanf("%d",&N))
    {
        for(ll i=0;i<N;i++)
        {
            scanf("%d",&a[i]);
        }
        sort(a,a+N);
        k=ceil(N*(N-1)/4.0);//这儿呢,k就是要找的差值是升序序列中的第几个
        max_=a[N-1]-a[0];
        min_=0;
        while(min_<=max_)
        {
            mid=(min_+max_)/2;
            if(judge())
                min_=mid+1;
            else
                max_=mid-1;
        }
        printf("%d\n",min_);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/86598373