Hoodlum1980 (fafa)'s Technological Blog

Languages mainly using and digging: C / CPP, ASM, C#, Python. Other languages:Java.

博客园 首页 新随笔 联系 订阅 管理

    ZOJ 1601:Integer Approximation

    链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1601

    题意:对于一个给定的浮点数A(0.1 <= A <= 10)和一个最大值 L(1 <= L <= 100,000),在 1 到 L  之间找出最佳的两个整数 N 和 D (1<= N, D <= L),使得 N / D 的结果和 A 最接近(即 | A - N / D | 最小)。例如对于圆周率 pi = 3.14159265358979,在 1 到 10000 之间 355 / 133 的结果最接近 pi 。

 

    分析:从直观上来看,除了“穷举式”的依次寻找,似乎并没有什么特别的捷径可寻。而且题目中对于 L 的值较小,不会超过 10w,这也暗示我们解法就是在这个范围内做一个完全搜索。

    显然如果除法的结果是相同的,则整数越小越好,因此我们应从小到大搜索,这样后续的较大的等效除法就不会覆盖前面的。注意起始搜索点要根据 A 和 1 之间的关系而定。例如如果 A < 1,这时候 N < D。因此应该先令 N = 1。反过来如果 A > 1,应该先令 D = 1。为了效率,我们应该以比较小的那个数字为基础进行逐个搜索,因为另一个数字计算后具有放大作用。所以用比较小的数字搜索的话,循环次数会小于基于比较大的数字进行搜索。但下面的代码,为了简单起见,一律基于 D 进行逐个搜索,并没有区别对待。

    例如如果 A = 0.1,则我们从 N = 1, D = 10 开始搜索,基于 D 进行累加,N = D * 0.1 会变化的很慢,将降低效率。因此这时候应该基于 N 搜索即累加 N 。如果调整 D 的步进值为 max( 1, (int)(1/A + 0.5) ),则要从数学上分析如何取整的问题,也就是对 D 采用一个固定的步进值和基于 N 搜索未必是等价的(因为 D 作为整数,和 N/A 之间存在误差),比较麻烦。 

    下面的代码中,y / x 是我们要探查的当前整数除法,即 y 对应的是 N,x 对应的是 D。err 是 y / x 和浮点数 A 之间的相对误差。在选取第一对整数时,务必要注意 L 的限制。例如如果 A = 0.1,则不考虑 L 我们选取的第一对整数应该是 1/10。但是如果 L = 5,那么 1/10 是不可能选到的,第一对整数就应该是 1 / 5。

 

zoj1601
#include <stdio.h>
#include <math.h>

void FindResult(double A, int L, int* pN, int* pD)
{
    double minerr = 100000, err;
    int x, y;

    if(A >= 1)
    {
        x = 1;
        y = (int)(x * A + 0.5);
        if(y > L) y = L;
    }
    else
    {
        y = 1;
        x = (int)(y / A + 0.5);
        if(x > L) x = L;
    }
    while(x <= L && y <= L)
    {    
        err = fabs(x * A - y);
        if(err < minerr)
        {
            *pN = y;
            *pD = x;
            minerr = err;
        }
        ++x;
        y = (int)(x * A + 0.5);
    }
}

int main(int argc, char* argv[])
{
    double A;
    int L, N, D;
    while(scanf("%lf %ld", &A, &L) != EOF)
    {
        FindResult(A, L, &N, &D);
        printf("%ld %ld\n", N, D);
    }
    return 0;
}

 

posted on 2012-06-03 20:49  hoodlum1980  阅读(333)  评论(0编辑  收藏  举报