二分答案

题目要求

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 非常想得到那只可爱的猫咪,你能帮帮他吗?

求解思路

为了降低时间复杂度,采用二分答案+枚举+二分查找的方法。
复杂度为O(logXi)+O(nlog(n))=O(nlog(n)logXi)
对于每个二分答案P,计算名次时为枚举Xi,二分查找满足Xj<=Xi+P,i<j的Xj的个数,并累加。
关键点为二分答案时判断答案的依据变为寻找一个x,使大于等于x的元素个数小于等于(数组长度-中位数位置),二分求得的最小的一个x,x的前一个不满足大于等于y的元素个数小于等于(数组长度-中位数位置)的元素y就是答案。
因为使用的二分结束条件为l<r,而移动方式为

if (sum <= len)  r = middle;
		else l = middle + 1;

所以结束时l=r,也就是说此时大于等于l的元素个数为(数组长度-中位数位置),因为只有大于等于x的元素个数小于(数组长度-中位数位置)时,x才会加一,所以此时的x-1一定是新数组的一个,并且是最大的不满足大于等于该元素的元素个数小于等于(数组长度-中位数位置)的元素,也就是答案!

举个栗子
如果数组为2 4 6 8 10
新数组就是 2 2 2 2 4 4 4 6 6 8
中位数为4

  • 如果从前往后直接计算名次的话,小于等于4的元素个数为7,这样4就被pass了
  • 如果按上述计算大于等于x的元素个数的话,就是5的时候对应为3,4
    的时候对应为6,二分答案到4的时候,6>5,所以4+1=5,结束二分答案,最终答案为5-1,也就是4.
  • 当然,也可以从前往后计算,找到第一个不满足小于等于x的元素个数小于中位数-1的元素。
代码
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
int n, len;
int cat[100005];

int findj(int _i, int p)
{
	int l = _i+1,r = n, ans = -1;
	while (l <= r)
 //   for(int j=0;j<n;j++)
	{
		int middle = (l + r) >> 1;
		if (cat[middle] >= cat[_i] + p)
		{
			ans = middle;
			r = middle - 1;
		}
		else  l = middle + 1;
	}

	return ans;
}

int findans()
{
	int l = 0, r = cat[n]-cat[1]; 
	while (l < r)
	{
		int middle = (l + r) >> 1;
		int sum = 0; int psum=0;
		for (int i = 1; i <= n; i++)//计算名次 
		{
			int p;p= findj(i,middle);
			if(p!=-1)
			sum =sum + (n-p)+1;	
		}
                sum <= len)  r = middle;
		else l = middle + 1;
	}
	
	return l-1;
}
int main()
{
	while (scanf("%d", &n) != EOF)
	{
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &cat[i]);
		}
		sort(cat + 1, cat + 1 + n);
		len = (n*(n - 1) / 2)-(((n*(n - 1) / 2)+1)/2);
		printf("%d\n", findans());
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值