二分答案求中位数

题目
TT 的神秘礼物

TT 是一位重度爱猫人士,每日沉溺于 B 站上的猫咪频道。
有一天,TT 的好友 ZJM 决定交给 TT 一个难题,如果 TT 能够解决这个难题,ZJM 就会买一只可爱猫咪送给 TT。
任务内容是,给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。
TT 非常想得到那只可爱的猫咪,你能帮帮他吗?

Input
多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5

Output
输出新数组 ans 的中位数

Sample input
4
1 3 2 4
3
1 10 2

Sample output
1
8

解题思路
二分答案比较抽象,理解起来比较困难。
我们先把数组给排好序 , 这样就便于去掉绝对值。
然后我们知道, 每两个数都可以有一个差值,可以成为新数组的一部分,所以我们就算出 新数组一共有 ( n-1+1 ) * ( n-1) / 2 个,即 n* (n-1) /2。
所以我们就可以得到新数组中中位数的位置 N = ( n*(n-1)/2 + 1)/2 ;
然后,我们把答案的区间初始化为 [ 0 , max ] ,其中 max = cat[n] - cat[1] ;
我们二分这个区间,不断地缩小区间,直到 L > R ,便得到我们要求的中位数。
那么这时,困难在于二分时如何进行判断?
我们的方法是, 定义一个juge ( mid ) 函数 ,其中mid = 区间的中点。
[ 我们需要判断 cat[i] - cat[j] <= mid 的个数,即为 mid 在 新数组里面的位置,当个数大于 N 时,它便在中位数右边,小于N,则在中位数左边 ,我们用sum 记录这个个数]
我们在这个函数里面循环 cat数组 ,然后计算,返回一个bool值。

代码实现

#include <cstdio>
#include <algorithm>
using namespace std;
long long cat[100005];
long long n;
long long N;

bool juge(long long x){
	long long sum=0;
    for( int i=1;i<=n;i++)
    {
    	sum+=lower_bound(cat+i,cat+n+1,cat[i]+x+1)-(cat+i)-1;  //找到最后一个小于cat[i]+x+1的地址
    	if( sum >=N )                                //然后减去cat+i的地址,得到的是小于cat[i]+x+1
              return 1;                                     //的个数,将他们都加起来,就得到mid在新数组中的位置
	}
	return 0;
}
int main(){
    while (scanf( "%lld",&n)!=EOF ){
    	if( n == 0) {printf("0\n");  continue;}	
    	for( int i=1;i<=n;i++)
    		scanf("%lld",&cat[i]);			
    	N = ( n*(n-1)/2 + 1)/2;	
    	sort(cat+1,cat+n+1); 
    	long long l = 0, r = cat[n]-cat[1];
    	while( l < r )
    	{  	    
    		long long mid = (l+r) >> 1;
    		if( juge(mid) ) r= mid;
    		else l = mid + 1;
		}
    	printf("%lld\n",l);
	} 
    return 0;
}

小结
我在juge函数里借鉴了网上的方法,用了lower_bound()函数,我看了这个函数的源码,本质上也是二分的,用在这里很合适。但是如果不想用这个函数的话,也可以自己写二分, find_l()函数,返回最后一个小于x的值的位置,再减去 i ,就可以得到循环到 i 的符合要求的个数,然后加起来得到sum,也可以求出mid 的名次 ,最后便可以求得中位数。 【本质上是不断缩小区间,得到值】我觉得答案二分好难啊,刚开始坐在电脑前坐了一个下午,都没想明白为什么可以二分答案 , 后来才想明白二分的是答案的区间, 去 逼近我们要求的那个答案 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值