第K小分数@hihoCoder

#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;
}

题目链接

猜你喜欢

转载自blog.csdn.net/qq_39021458/article/details/81127987