思路:
简单转化之后,此题就是给你一个小数,叫你输出值最接近的分数,划成最简形式。
考虑二分分数。
若不知道起始范围,则可以从:
0
1
−
1
0
(
+
o
o
)
\frac{0}{1} - \frac{1}{0}(+oo)
10−01(+oo)
进行二分。
出于效率和最简分数的考虑,我们每次二分的值不是求端点的平均数。
比如对于
[
a
b
,
c
d
]
[\frac{a}{b},\frac{c}{d}]
[ba,dc],我们二分的新的分数为:
a
+
c
b
+
d
\frac{a+c}{b+d}
b+da+c
然后进行比较而决定左移右移即可。
另外我们可以先算答案分数的整数值,来缩小起始的二分范围。
如果第一个不小于该小数的整数值为val,则范围应该为:
[
(
v
a
l
−
1
)
1
,
v
a
l
1
]
[\frac{(val-1)}{1},\frac{val}{1}]
[1(val−1),1val]
代码:
#include<cstdio>
using namespace std;
typedef long long ll;
typedef long double lb;
ll k,lz,rz,lm,rm,val,A,B;
lb Mn,aim;
lb Abs(lb x){
return x>0?x:-x;
}
int main(){
int T;scanf("%d",&T);
while(T--){
ll k;
scanf("%I64d",&k);
val = 1;
while(val*val*val < k*k) val++;
if(val*val*val == k*k){
printf("%I64d/1\n",val);
continue;
}
lz = val-1,lm = 1;aim = k*k;
rz = val,rm = 1;Mn = 1e60;
while(1){
ll a = lz + rz,b = lm + rm;
if(b > 100000) break;
lb u = a,v = b;
lb now = u*u*u/v/v/v;
if(Abs(now-aim) < Mn){
Mn = Abs(now-aim);
A = a,B = b;
}
if(now > aim){rz = a,rm = b;}
else {lz = a,lm = b;}
}
printf("%I64d/%I64d\n",A,B);
}
return 0;
}