数学题 贪心+二分答案

Description

现在有两个数组 AA 和 BB, 分别包含 xx 与 yy 个元素。

定义一个新的数组 CC, CC 中包含 x×yx×y 个元素,为 AA 中所有元素除以 BB 中所有元素。

即 新集合为 cc=abaAbB{c∣c=ab,a∈A,b∈B} 。特殊地,CC 为多重集合。

请求 CC 数组的第 kk 大数。

Input

第一行一个整数 TTT3T≤3)表示方案数。

对于每个方案:

第一行三个整数 n,m,kn,m,k0<nm1000000<kn×m0<n,m≤100000,0<k≤n×m )

第二行 nn 个正整数;

第三行 mm 个正整数。

数组中元素 <108<108

Output

对于每个方案,输出一行:

数组 CC 的第 kk 大数。结果四舍五入到两位小数。

Sample Input

2
5 5 3
1 2 3 4 5
2 3 4 5 6
5 5 2
1 2 3 4 5
2 3 4 5 6

Sample Output

1.67
2.00
题解:这个题乍一看想FFT,其实没有那么麻烦,只是个简单的二分答案。

如果一个数是第K大的数,那么一定存在至少K个数小于等于它,把这个作为二分的条件。

如何判断有K个数小于等于它呢,好像不是很好判断,我们转换为计算大于x的数的个数。

我们可以把A数组和B数组排序,然后用双指针,指针Pa指向数组A的左端,指针Pb指向数组B的左端,这样固定Pa,把Pb像右移动,直到(*Pa)/(*Pb) < x为止,然后Pa向右移动一个,再循环这个操作,直到Pa到头或者Pb到头(可以证明Pb始终不需要掉头)

check函数的代码:

int check(double x)
{
	int tot = 0;
	int pos = 0; 
	for(int i = 0;i < N;i++)
	{
		while(pos < M && (double)A[i]/(double)B[pos] >= x) pos++;
		tot += pos;
	}
	return tot; 
}
代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 100005;
const double EPS = 0.001;
int A[MAX],B[MAX];
int N,M,K;
int check(double x)
{
	int tot = 0;
	int pos = 0; 
	for(int i = 0;i < N;i++)
	{
		while(pos < M && (double)A[i]/(double)B[pos] >= x) pos++;
		tot += pos;
	}
	return tot; 
}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&N,&M,&K) ;
		for(int i = 0;i < N;i++) scanf("%d",&A[i]);
		for(int i = 0;i < M;i++) scanf("%d",&B[i]);
		sort(A,A + N);sort(B,B + M);
		double l = 0,r = A[N-1];
		while(r - l > EPS)
		{
			double mid = (r + l) /2;
			if(check(mid) < K) r = mid;//检查大于mid的数有多少个 
			else l = mid; 
		}
		printf("%.2f\n",l);
	}
	return 0;
 } 
 /*
 2
5 5 3
1 2 3 4 5
2 3 4 5 6
5 5 2
1 2 3 4 5
2 3 4 5 6
 */




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值