#1692 : 第K小分数
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定N个不同的质数P1, P2, ... PN。用它们作为分目可以组成(P1-1) + (P2-1) + ... (PN-1)个分数:
1/P1, 2/P1, 3/P1, ..., P1-1/P1, 1/P2, 2/P2, 3/P2, ... P2-1/P2, ... 1/PN, 2/PN, ... PN-1/PN
请帮助小Ho求出其中第K小的分数。
输入
第一行包含两个整数N和L。
以下N行每行包含一个质数Pi。
对于70%的数据,1 ≤ N ≤ 100, 1 ≤ K ≤ 1000000, 2 ≤ Pi ≤ 100000
对于100%的数据, 1 ≤ N ≤ 1000, 1 ≤ K ≤ 1000000000, 2 ≤ Pi ≤ 1000000000
输出
输出一个分数表示答案
样例输入
3 4 2 3 5样例输出
1/2
题目分析:(使用二分方法是借鉴了一位老师的做法,自己也是第一次使用二分方法解决这类问题,使用不熟练,如果感觉不合适可以在网上查看其他人的做法)
二分思路:(很奇特,不太容易想到,可以作为一种经验)假设有一个 [0,1] 之间的浮点数,如 x=0.5,那么当以一个质数Pi作为分母时,有 (int)x*Pi 个分数是小于x的。如 x=0.5,Pi=5时,有 1/5、2/5两个数是小于x的。
那么我们可以想到,找到一个 实数x ,设 cnt = (int)x*P[i](0<=i<n),当 cnt == k时,题目中要求的解就可以转化为寻找小于等于 x 的分数中,最大的那个分数。此处配一个图,是跟该题题意相符的,出处为前面提到的老师的博客:
黄色块代表分数1/2;绿色块从左到右代表分数1/3、2/3;以下同理。
结合上图,我们要找的就是黄色虚线左边的最大的分数是哪一个,这个怎么实现的会在代码段讲解。
#include <iostream>
#include <algorithm>
#define MAXN 1003
using namespace std;
int n,k;
int P[MAXN];
int main(){
cin >> n >> k;
for(int i=0;i<n;++i) cin >> P[i];
double l = 0.0, r = 1.0; //因为该题中分数的范围都是在(0,1)之间的,所以l,r的初始化如此
while(l < r){
double m = (l+r)/2.0; //m就是题目分析中的x
long long cnt = 0; //cnt用来计算 <= m 数量和
int bestz = -1, bestm = -1; //bestz代表最优答案的分子,bestm代表分母
for(int i=0;i<n;++i){ //枚举每一个P[i]
int x = m*P[i];
cnt += x;
if(bestz == -1){
bestz = x;
bestm = P[i];
}
if((long long)x*bestm > (long long)bestz*P[i]){ //这个地方是一个坑点,WA了好多次,当比较两个分数时,
bestz = x; //最好是转化成积的形式比较,避免精度丢失
bestm = P[i];
}
}
if(cnt == k){
cout << bestz << "/" << bestm << endl; //如果cnt == k,直接输出答案
return 0;
}
if(cnt < k){ //否则,改变边界
l = m;
}
else r = m;
}
return 0;
}