hiho 1692 第k小分数(实数上的二分答案)

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
链接:http://hihocoder.com/problemset/problem/1692
#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
题解:
由于p是质数,所以每个分数都不可约分,由于对于每一个1/P1, 2/P1, 3/P1, …, P1-1/P1序列都是递增,我们可以二分答案,算出小于等于二分答案的数有多少个,当个数等于k时即二分答案接近要求的答案,记录最接近的分子和分母,即为答案

#include
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1000;
int n,k;
ll p[1001],bestz,bestm;
// p[i] 是素数所以不可约分,所以每个数都互不相同 
ll ok(double mid){
	ll cnt = 0;
	fo(i,1,n){
		ll x = mid*p[i]; // 向下取整 
		cnt += x;
		if(bestz==-1){
			bestz = x;
			bestm = p[i]; 
		}
		if((ll)bestz*p[i]<(ll)x*bestm){
			bestz = x;
			bestm = p[i];
		}
	}
	return cnt; 
}
void solve(){
	double eps = 1e-9; // 小于相邻两数最小差 
	double l=0.0,r=1.0,mid;
	while(true){
		mid = (l+r)/2;
		bestz=-1,bestm=-1; // 一定要记住每次调用ok函数前初始化,wa了好几遍 
		ll cnt = ok(mid);
		if(cnt==k){
			cout<<bestz<<'/'<<bestm<<endl;
			return;
		}
		if(cnt>k){ // 如果有大于k个,缩小范围 
			r = mid;
		}else{
			l = mid;
		}
	}
}
int main(){
	while(scanf("%d%d",&n,&k)==2){
		fo(i,1,n)scanf("%lld",&p[i]);
		solve();
	} 
	return 0;
}

你可能感兴趣的:(二分查找,ACM算法日常)