两个有序数组的第 K 小乘积(第k小转化为二分)

这种题一个套路问题
https://www.cnblogs.com/lipu123/p/15435169.html

几乎每一个人都用 乘法表。但是你能在乘法表中快速找到第 k 小的数字吗?
乘法表是大小为\(m x n\)的一个整数矩阵,其中\(mat[i][j] == i * j\)(下标从 1 开始)。
给你三个整数\(m、n 和 k\),请你在大小为\(m x n\)的乘法表中,找出并返回第\(k\)小的数字。

示例 1:
image

输入:m = 3, n = 3, k = 5
输出:3
解释:第 5 小的数字是 3 。

示例 2:
image

输入:m = 2, n = 3, k = 6
输出:6
解释:第 6 小的数字是 6 。

提示:
\(1 <= m, n <= 3 * 10^4\)
\(1 <= k <= m * n\)

这个题直接求得话,会超时,因为n和m都比较大。这种求个数得题可以转化为二分。然后judge得时候就是求有多少个数小于mid,然后求个数大于k得最小的一个就行。这也转化成了大于k的最小值,也是一个二分。

然后就是求有多少数字不超过 \(x\)

我们可以遍历乘法表的每一行,对于乘法表的第 \(i\) 行,其数字均为 \(i\) 的倍数,因此不超过 \(x\) 的数字有 \(\min(\Big\lfloor\dfrac{x}{i}\Big\rfloor,n)\)个,所以整个乘法表不超过 \(x\) 的数字个数为

\[\min(\Big\lfloor\dfrac{x}{i}\Big\rfloor,n) \]

由于$ i≤\lfloor\dfrac{x}{i}\rfloor$时 \(\lfloor\dfrac{x}{i}\rfloor ≥n\),上式可化简为

\[\Big\lfloor\dfrac{x}{n}\Big\rfloor\cdot n + \sum_{i=\Big\lfloor\dfrac{x}{n}\Big\rfloor+1}^{m} \Big\lfloor\dfrac{x}{i}\Big\rfloor \]

由于 \(x\) 越大上式越大,\(x\) 越小上式越小,因此我们可以二分 \(x\) 找到答案,二分的初始边界为乘法表的元素范围,即 \([1,mn]\)

class Solution {
public:
    int findKthNumber(int m, int n, int k) {
        int l=1,r=m*n,ans=1;
        while(r>=l){
    		int mid=(l+r)/2;
    		int cnt=mid/n*n;
    		for(int i=mid/n+1;i<=m;i++){
    			cnt+=mid/i;
			}
			if(cnt>=k){
				ans=mid;
				r=mid-1;
			}
			else{
				l=mid+1;
			}
		}
		return ans;
    }
};

两个有序数组的第 K 小乘积&两个有序数组的第 K 大乘积
https://leetcode.cn/problems/kth-smallest-product-of-two-sorted-arrays/description/
https://codefun2000.com/p/P1052
第一个是第k小乘积,第二个是第k大乘积
通过二分查找第 \(k\) 小的乘积 \(p\),每次判定时枚举 nums1 中的数 \(a\),通过二分再次判断 nums2 中有几个数 bbb 满足 \(ab \le p\)。注意需要对 a>0,a<0 和a = 0三种情况分别讨论。复杂度 \(\mathcal{O}(n\log n\log A)\)

#include<iostream>
#include<algorithm>
using namespace std;
//article
typedef long long ll;
const int maxn=5e5+100;
ll a[maxn],b[maxn];
ll n,m,k;
ll get(ll mid){//小于等于mid的个数 
	ll sum=0;
	for(int i=1;i<=n;i++){
		ll x=a[i];
		if(x<0){
			ll L=1,R=m,ans=m+1;//大->小 
			while(R>=L){
				ll mid2=(R+L)/2;
				if(x*b[mid2]<=mid){
					R=mid2-1;
					ans=mid2;
				}
				else{
					L=mid2+1;
				}
			}
			sum+=(m-ans+1);
		}
		else if(x==0){
			if(mid>=0){
				sum+=m;
			}
		}else if(x>0){
			ll L=1,R=m,ans=0;//小->大 
			while(R>=L){
				int mid2=(R+L)/2;
				if(x*b[mid2]<=mid){
					L=mid2+1;
					ans=mid2;
				}
				else{
					R=mid2-1;
				}
			}
			sum+=ans;
		}
	}
	return sum;
} 
int main(){
	//第k小 
    cin>>n>>m>>k;
	//k=m*n-k+1;去掉这个注释就是第k大
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=m;i++)
        cin>>b[i];
    sort(b+1,b+m+1);
    ll l=-1e13,r=1e13,ans=0;
    while(r>=l){
		ll mid=(l+r)/2;
		ll sum=get(mid);
		if(sum>=k){
			ans=mid;
			r=mid-1;
		}else{
			
			l=mid+1;
		}
	}
	cout<<ans<<endl;
}
/*
3 4 5
5 -4 0
3 4 5 1


9
-20 -16 -12 -4 0 0 0 0 5 15 20 25  
*/
/*
2 2 1
-100000 100000
-100000 100000

-10000000000 -10000000000 10000000000 10000000000
*/
typedef long long ll;
const int maxn=1e5+100;
class Solution {
int a[maxn],b[maxn],n,m;
public:
    ll get(ll mid){//小于等于mid的个数 
        ll sum=0;
        for(int i=1;i<=n;i++){
            ll x=a[i];
            if(x<0){
                ll L=1,R=m,ans=m+1;//大->小 
                while(R>=L){
                    ll mid2=(R+L)/2;
                    if(x*b[mid2]<=mid){
                        R=mid2-1;
                        ans=mid2;
                    }
                    else{
                        L=mid2+1;
                    }
                }
                sum+=(m-ans+1);
            }
            else if(x==0){
                if(mid>=0){
                    sum+=m;
                }
            }else if(x>0){
                ll L=1,R=m,ans=0;//小->大 
                while(R>=L){
                    int mid2=(R+L)/2;
                    if(x*b[mid2]<=mid){
                        L=mid2+1;
                        ans=mid2;
                    }
                    else{
                        R=mid2-1;
                    }
                }
                sum+=ans;
            }
        }
        return sum;
    } 
    long long kthSmallestProduct(vector<int>& nums1, vector<int>& nums2, long long k) {
        n=nums1.size();
        m=nums2.size();
        for(int i=1;i<=n;i++){
            a[i]=nums1[i-1];
        }
        for(int i=1;i<=m;i++){
            b[i]=nums2[i-1];
        }
        sort(b+1,b+m+1);
        ll l=-1e13,r=1e13,ans;
        while(r>=l){
            ll mid=(l+r)/2;
            ll sum=get(mid);
            if(sum>k){
                ans=mid;
                r=mid-1;
            }else if(sum==k){
                ans=mid;
                r=mid-1;
            }else{
                l=mid+1;
            }
        }
        return ans;
    }
};
posted @ 2024-06-13 16:46  lipu123  阅读(59)  评论(0)    收藏  举报