第K大数-二分原来还可以这样

题目:

数组A和数组B,里面都有n个整数。数组C共有n^2个整数,分别是A[0] * B[0],A[0] * B[1] ......A[1] * B[0],A[1] * B[1]......A[n - 1] * B[n - 1](数组A同数组B的组合)。求数组C中第K大的数。
例如:A:1 2 3,B:2 3 4。A与B组合成的C包括2 3 4 4 6 8 6 9 12共9个数。
Input
第1行:2个数N和K,中间用空格分隔。N为数组的长度,K对应第K大的数。(2 <= N <= 50000,1 <= K <= 10^9)
第2 - N + 1行:每行2个数,分别是A[i]和B[i]。(1 <= A[i],B[i] <= 10^9)
Output
输出第K大的数。
Input 示例
3 2
1 2
2 3
3 4
Output 示例
9
 

题目分析:

二分A[0]*B[0] -- mid--  A[N-1]*B[N-1],然后求出小于mid的数,用小于mid的数的个数与N*N-K+1比较,一直到最后二分跳出即为答案。
     之前一直不明白为何能够二分到准确的答案,因为如果求第3大,答案应该是8,但是7是不是也满足情况。实际上这是要看二分什么时候跳出。
 如果你发现有3个>=K的就跳出了,那么肯定不是最终答案。但是如果你到l>r的时候再跳出,二分过程中肯定有一刻指向的是序列中的某个值。
举例:还是找上面例子中第3大
k=3
l=2,r=12 mid = 7
大于=7的有3个,3>=k; 注意这个时候不能跳出而是另l=mid+1=8
l=8,r=12,mid=10
大于=10的只有1个<k,那么r=mid-1=9;
l=8,r=9,mid=8
这个同第一种情况,l=mid+1=9;
l=9,r=9,mid=9
>=9的个数2个<k,那么r=mid-1=8跳出循环(实际上跳出循环肯定是有一个所指即所求)
那么实际l=8 r=9时所指的就是答案。

代码:

//直接计算第K大
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 50002;
__int64 a[MAXN],b[MAXN];
__int64 N,K;
__int64 cnt(__int64 mid)
{
	__int64 ret = 0; 
	__int64 j = 0;
	for (__int64 i = N-1; i >= 0; -- i)
	{
		while(j<N)
		{
			if (b[j]*a[i] < mid)
				++j;
			else
				break;
		}
		ret += N-j;
	}
	return ret;
}
void binarySearch()
{
	__int64 l = a[0]*b[0];
	__int64 r = a[N-1]*b[N-1];
	__int64 ans = 0;
	while (l <= r)
	{
		__int64 mid = (l+r)>>1;
		if (cnt(mid) >= K)
		{
			ans = mid;
			l = mid + 1;
		}
		else
			r = mid - 1;
	}
	cout << ans << endl;
}
int main()
{
	
	while (cin >> N >> K)
	{
		//K = N*N - K + 1;
		for (int i = 0; i < N; ++ i)
		{
			cin >> a[i] >> b[i];
		}
		sort(a,a+N);
		sort(b,b+N);
		binarySearch();
	}
	return 0;
}


//计算第N*N-K+1小数,这里转换一下
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 50002;
__int64 a[MAXN],b[MAXN];
__int64 N,K;
__int64 cnt(__int64 mid)
{
	__int64 ret = 0; 
	__int64 j = N-1;
	for (__int64 i = 0; i < N; ++ i)
	{
		while(j>=0)
		{
			if (b[j]*a[i] > mid)
				--j;
			else
				break;
		}
		ret += j+1;
	}
	return ret;
}
void binarySearch()
{
	__int64 l = a[0]*b[0];
	__int64 r = a[N-1]*b[N-1];
	__int64 ans = 0;
	while (l <= r)
	{
		__int64 mid = (l+r)>>1;
		if (cnt(mid) >= K)
		{
			ans = mid;
			r = mid - 1;
		}
		else
			l = mid + 1;
	}
	cout << ans << endl;
}
int main()
{
	
	while (cin >> N >> K)
	{
		K = N*N - K + 1;
		for (int i = 0; i < N; ++ i)
		{
			cin >> a[i] >> b[i];
		}
		sort(a,a+N);
		sort(b,b+N);
		binarySearch();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值