题目描述
有两个序列a,b,它们的长度分别为n和m,那么将两个序列中的元素对应相乘后得到的n*m个元素从大到小排列后的第k个元素是什么?
输入
输入的第一行为一个正整数t (t<=10),代表一共有t组测试数据。
每组测试数据的第一行有三个正整数n,m和k(1<=n, m<=100000,1<=k<=n*m),分别代表a,b序列的长度和所求元素的下标。第二行为n个正整数代表序列a。第三行为m个正整数代表序列b。序列中元素的大小在[1,100000]之间。
输出
对于每组测试数据,输出一行包含一个整数代表第k大的元素是多少。
样例输入
3
3 2 3
1 2 3
1 2
2 2 1
1 1
1 1
2 2 4
1 1
1 1
样例输出
3
1
1
这道题实际上有一定的难度,我这题一开始的想法便是开辟一个新的大数组,接收mn个数据元素,但是实际上这样很可能造成运行错误。因为能够开辟的数组大小有限,但是题目给定的要求中mn可能太大了,因此导致错误,以下是我的错误代码:
//错误代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<numeric>
#define ll long long
using namespace std;
int a[1000],b[100000],c[10000000];
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
int n,m,k,t,i,j,x;
cin>>t;
while(t--)
{
x=0;
cin>>n>>m>>k;
for(i=0;i<n;i++)cin>>a[i];
for(i=0;i<m;i++)cin>>b[i];
for(i=0;i<n;i++)
for(j=0;j<n;j++)
{
c[x]=a[i]*b[j];
x++;
}
sort(c,c+x,cmp);
cout<<c[k-1]<<endl;
}
return 0;
}
运行结果是这样的
当然这里应该开个long long才对
但是实际上最现实的做法不应该是开辟新数组,而是利用二分法,具体代码如下
//正确代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<numeric>
#define ll long long
using namespace std;
ll n,m,k;//定义一个全局变量避免处理一些不必要的函数关系
ll a[100005],b[100005];
bool cmp(ll a,ll b)
{
return a>b;
}
ll fun(ll s)
{
ll num=0;
ll i=0;//a数组的最大值下标
ll j=n-1;//b数组的最小值下标
for(;i<n&&j>=0;i++)
{
if(a[i]*b[j]>=s)
num+=j+1;
else
{
while(j>=0&&a[i]*b[j]<s)
j--;
if(j>=0)
num+=j+1;
}
}
return num;
}
ll aa()
{
ll left=a[n-1]*b[n-1];
ll right=a[0]*b[0];//利用主函数中的排序,轻松地把即将调用的二分法函数中的最小值最大值范围确定
ll res=0;
while(left<=right)//二分法
{
ll mid=(left+right)/2;
if(fun(mid)>=k)//利用fun函数寻找下标
{
left=mid+1;
res=mid;
}
else
right=mid-1;
}
return res;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m>>k;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(ll i=0;i<n;i++)
cin>>a[i];
for(ll i=0;i<m;i++)
cin>>b[i];
sort(a,a+n,cmp);
sort(b,b+m,cmp);//先对两个数组进行排序
ll s=aa();//利用二分法
cout<<s<<endl;
}
return 0;
}