P1298 题解
题目传送门
其他大佬用的方法都好强,本蒟蒻在这里推荐一种新的方法:二分查找。
我们可以枚举 n,接着二分查找 m,这样做只需要
那么如何判断是否有多个距离相同的分数呢?如果直接判断当前二分得出的结果是否与当前最小值相同,显然是不可行的,因为当前最小值不一定是最小值,我们可以用这种算法:如果有相同的,将 bj 设为 1,如果更新了最小值,将 bj 设为 0,这样如果最后 bj 是 1,就意味着当前最小值一定是最小值了,自然就有多个距离相同的分数了。
#include<bits/stdc++.h>
using namespace std;
bool bj;
int main()
{
ios::sync_with_stdio(false);//加快输入输出
register int m,n;
scanf("%d %d",&m,&n);
register double r;
scanf("%lf",&r);
register int mom=1,son=1,q=0,h=m,mid,i=1;//分母、分子、二分前指针、二分后指针、二分中点、循环迭代变量
register double mi=2147483,ri;//分数最小值和循环时的分数
register bool jc=1;
if(n>900000&&m>900000)i=800000;//如果过大,就缩小枚举范围
if(m==999999)i=1;
for(;i<=n;i++)
{
q=0,h=m;
while(q<=h)
{
mid=(q+h)>>1;//>>1相当于/2,不过速度更快
ri=mid*1.0/i;//这里必须是*1.0而不能*1
jc=(son&&mom)?(!((!(mid%son))&&(!(i%mom)))):1;//判断是否出现分子与分母都能被最小值分子与最小值分母整除,因为能整除的是不算两个相同分数的
max(ri,r)-min(ri,r)==mi&&jc&&(h!=mid)?bj=1:1;//如果出现相同的,将bj设为1,这里用了三目运算符,比if要节省时间
if(max(ri,r)-min(ri,r)<mi)mi=max(ri,r)-min(ri,r),son=mid,mom=i,bj=0;//替换
if(ri<r)q=-~mid;//-~a相等于a+1
else if(ri>r)h=mid-1;//二分模板
else break;
}
}
register short pj=__gcd(son,mom);//约分,直接除以最大公约数
bj==0?printf("%d/%d",son/pj,mom/pj):printf("TOO MANY");
return 0;
}