Leetcode 786. 第 K 个最小的素数分数
题目
一个已排序好的表 A,其包含 1 和其他一些素数. 当列表中的每一个 p<q 时,我们可以构造一个分数 p/q 。
那么第 k 个最小的分数是多少呢? 以整数数组的形式返回你的答案, 这里 answer[0] = p 且 answer[1] = q.
示例:
输入: A = [1, 2, 3, 5], K = 3
输出: [2, 5]
解释:
已构造好的分数,排序后如下所示:
1/5, 1/3, 2/5, 1/2, 3/5, 2/3.
很明显第三个最小的分数是 2/5.
输入: A = [1, 7], K = 1
输出: [1, 7]
注意:
- A 长度的取值范围在 2 — 2000.
- 每个 A[i] 的值在 1 —30000.
- K 取值范围为 1 —A.length *(A.length - 1) / 2
题解
二分查找
显然,最小的可能分数left为(double)A[0]/A.back()、最大right为1。我们在[left,right]区间中进行二分查找。我们统计比当前分数mid小的可能分数个数smallerNum,并找到最接近mid的能构造出来的分数(比mid大)。从而更新查找区间,如果smallerNum < K,则在[mid,right]中找;否则,则在[left,mid]上查找。
对于smallerNum的确定:我们只要分母越大或分子越小,则分数越小,因此我们只需要推进一个窗口,窗口右边界j作为分母,左边界作为i作为分子。显然,对于每个j,我们找到A[i+1]/A[j] < x的临界i,那么0~i都可以作为此时的分母,自然smallerNum += i+1;
详细过程见代码
代码
vector<int> ansNow;
int under(vector<int>&A, double x){ //确定比x小的分数个数
int n = A.size(),up=0,down=1;
int i=-1,cnt=0;
for(int j=1; j<n; j++){
while((double)A[i+1]/A[j] < x) i++;
cnt += i+1;
if(i>=0 && up*A[j] < A[i]*down){
up = A[i];
down = A[j];
}
}
ansNow[0] = up;
ansNow[1] = down;
return cnt;
}// 0.2 0.333 0.4 0.5 0.6
vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) {
double left = (double)A[0]/A.back(),right = 1;
ansNow = vector<int>(2,0);
vector<int> ans(2,0);
while(right-left > 1e-9){ //存在误差,因此只要区间足够小,我们就停止查找
double mid = (left+right)/2;
int smallerNum = under(A,mid);
//cout<<smallerNum<<" "<<mid<<endl;
if(smallerNum < K) left = mid;
else{
ans[0] = ansNow[0];
ans[1] = ansNow[1];
right = mid;
}
}
return ans;
}
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/k-th-smallest-prime-fraction
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。