1 条题解

  • 0
    @ 2025-5-22 13:52:59

    有理分数逼近问题题解

    题目分析

    问题描述:给定浮点数aa和整数LL,寻找分子NN和分母DD1N,DL1 \leq N,D \leq L),使得分数N/DN/Daa的绝对误差最小。若存在多个解,输出其中任意一个。

    输入格式:输入包含浮点数aa和整数LL1L1051 \leq L \leq 10^5)。
    输出格式:输出最优分数的分子和分母NN DD

    算法思路

    该问题属于有理分数逼近问题,代码采用贪心调整策略逐步寻找最优解:

    1. 初始化分子top=1top=1和分母bottom=1bottom=1,从最小分数开始搜索。
    2. 比较当前分数top/bottomtop/bottom与目标值aa的大小:
      • top/bottom<atop/bottom < a,则增大分子toptop(使分数接近aa)。
      • top/bottomatop/bottom \geq a,则增大分母bottombottom(使分数减小)。
    3. 每次调整后计算误差,记录误差最小的分数作为最优解。

    代码关键步骤

    1. 初始化:分子toptop和分母bottombottom11开始,误差minmin初始化为较大值(如1616)。
    2. 循环调整
      • 计算当前分数tmp=top/bottomtmp=top/bottom
      • tmp<atmp < a,说明分数偏小,增大分子toptop以接近aa
      • tmpatmp \geq a,说明分数偏大,增大分母bottombottom以减小分数。
    3. 误差记录:每次调整前比较误差,若当前误差更小,则更新最优解ansnans_nansdans_d

    示例分析

    输入3.14159263.1415926 10001000

    • 初始top=1,bottom=1top=1, bottom=1,分数1/1=1<3.14159261/1=1 < 3.1415926toptop增加到22
    • 分数2/1=2<3.14159262/1=2 < 3.1415926toptop增加到333/1=3<3.14159263/1=3 < 3.1415926)。
    • top=4top=4时,4/1=4>3.14159264/1=4 > 3.1415926,改为增大bottombottom22,分数4/2=2<3.14159264/2=2 < 3.1415926toptop增加到555/2=2.5<3.14159265/2=2.5 < 3.1415926)。
    • 继续调整,最终在top=22,bottom=7top=22, bottom=7时,分数22/73.14285722/7 \approx 3.142857,误差较小,成为最优解。

    复杂度分析

    • 时间复杂度O(L)O(L),最坏情况下需遍历toptopbottombottom11LL,总循环次数为O(L)O(L)
    • 空间复杂度O(1)O(1),仅使用常数级变量。

    关键点

    1. 贪心策略:通过动态调整分子和分母,使分数逐步逼近目标值aa,适用于LL较小的场景。
    2. 误差比较:每次调整前更新最小误差,确保记录最优解。
    3. 边界条件:当toptopbottombottom超过LL时终止循环,保证分数的分子和分母不超过限制。

    标程

    #include <iostream>
    using namespace std;
     
    int main(){
        double a, tmp, min = 16;
        int top, bottom, L, ans_n, ans_d;
        cin >> a >> L;
        top = bottom = 1;
        while (top <= L && bottom <= L) {
            tmp = (double)top / bottom;
            if (tmp < a) {
                if (a - tmp < min) {  // 误差更小则更新最优解
                    min = a - tmp;
                    ans_n = top;
                    ans_d = bottom;
                }
                top++;  // 分数小于a时增大分子
            } else {
                if (tmp - a < min) {  // 误差更小则更新最优解
                    min = tmp - a;
                    ans_n = top;
                    ans_d = bottom;
                }
                bottom++;  // 分数大于等于a时增大分母
            }
        }
        cout << ans_n << " " << ans_d << endl;
        return 0;
    }
    

    总结

    该算法通过贪心调整分子和分母,在O(L)O(L)时间内找到近似分数,逻辑简单且高效。尽管理论上最优有理逼近需使用连分数展开(如渐进分数),但此方法在实际应用中对多数情况有效,尤其适用于编程竞赛中的快速求解。

    • 1

    信息

    ID
    651
    时间
    1000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    1
    已通过
    1
    上传者