第k大数

题目描述
有两个序列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;
}

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最负责的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值