POJ 3579 Median 二分

一、题目大意

有1-n个数字x[i](1=

二、解题思路

题目的数量级是1e5,暴力枚举超时,用二分才能解决,其中二分的核心依据如下。

对数列中的元素进行二分,设二分的值为mid
每次找出数列中小于mid的元素数量,判断数列中小于mid的值是否小于一半
如果数列中小于mid的元素数量大于等于一半,那么mid取大了,缩小mid
如果小于一半,那么mid取小了,增大mid

直到找到一个临界值limit,使得当mid=limit时
数列中小于mid的元素数量不足一半
当mid=limit+1时
数列中小于mid的元素超过或者等于一半
那么不难看出,这个limit一定是中位数

那么接下来就是如果找到数列中小于mid的元素个数了,这其实也很简单,思路如下

我们首先对所有的元素x进行排序
对于每一个元素x[i],我们找到大于等于x[i]+mid的元素j,对于任意k∈(i,j),一定有
x[k]-x[i]

需要注意的是,在计算a[i]+mid和(left+right)/2时要当心不要溢出,我针对于这一步计算时开了64位整数

然后left的话取-1就好,right就取数组的极差+1(二分算出的mid∈[left+1,right-1])

三、代码

#include 
#include 
using namespace std;
typedef long long ll;
int arr[100007], inf = 0x3f3f3f3f, n, k;
void input()
{
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);
	}
	int count = n * (n - 1) / 2;
	k = count / 2;
	if (count % 2 != 0)
	{
		k++;
	}
	sort(arr, arr + n);
	arr[n] = inf;
}
bool judge(int mid)
{
	ll countLt = 0, countEq = 0;
	for (int i = 0; i < n; i++)
	{
		ll val = arr[i];
		val += mid;
		int rangeLt;
		if (val < inf)
		{
			rangeLt = lower_bound(arr, arr + n + 1, arr[i] + mid) - arr - i - 1;
		}
		else
		{
			rangeLt = n - i - 1;
		}
		countLt = countLt + rangeLt;
	}
	return countLt < k;
}
void binarySearch()
{
	int left = -1, right = arr[n - 1] - arr[0] + 1;
	while (left + 1 < right)
	{
		ll mid = left;
		mid += right;
		mid = mid >> 1;
		if (judge((int)mid))
		{
			left = mid;
		}
		else
		{
			right = mid;
		}
	}
	printf("%d\n", left);
}
int main()
{
	while (~scanf("%d", &n))
	{
		input();
		binarySearch();
	}
	return 0;
}

你可能感兴趣的:(算法)