数论--poj1930

题意:给你一个小数,求他的分数形式。

思路:

这题主要是给出一个无限循环小数,要求去求他所对应的具有最小分母的分数。无限循环小数转换为分数有

一定的规律和方法可循:

(1)假设输入小数为0.i1 i2 ... it j1 j2 ... jk,即j1 j2 ... jk是循环的部分 那么这个分数可以分两部分来计算即不循环的部分和循环的部分.


(2)先来看不循环的部分,这个非常简单.即为 i1i2 ... it / 10 ^ t,即不循环部分的分子分母分别为: upd = i1 i2 ... it; downd = 10 ^ t;

(3)再来看循环部分,这部分有点难.在数论中的典型做法是:

取分子up为:j1 j2 ... jk

分母down为: 10 ^ (t + k) - 10 ^ t

(4)那么最后所得的分数即为: upd / downd + up / down = (upd * down + downd * up) / (down * up)

(5)注意计算的时候要约分.另外由于题目没有明确给出循环部分的长度,所以需要自己枚举,然后计算出

所有的分数取分母最小的.另外要注意的是输入中含有全0的情况(输出0/1),这个太恶心了,因为题目明确说

了输入不全为0的,害我WA了好几次.

#include <iostream>  
#include <cmath>  
#include <cstring>  
using namespace std;  
char input[20];  
int spos, epos;  
__int64 minup, mindown;  
__int64 gcd(__int64 a, __int64 b)  
{  
    if(b == 0) return a;  
    else return gcd(b, a % b);  
}  
//利用最大公约数来约分  
void trim(__int64 &up, __int64 &down)  
{  
    __int64 gcdval;  
    while((gcdval = gcd(up, down)) != 1)  
    {  
        up /= gcdval;  
        down /= gcdval;  
    }  
}  
//判断是否全0  
bool allZero()  
{  
    for(int i = strlen(input) - 4; i >= 2; i--)  
        if(input[i] != '0') return false;  
    return true;  
}  
int main()  
{  
    while(scanf("%s", input) && strcmp(input, "0") != 0)  
    {  
        //当不全0时  
        if(!allZero())  
        {  
            mindown = -1;  
            epos = strlen(input) - 4;  //输入字符串数字位的结束位置  
            spos = 2;                         //小数点后面的起始位置  
            __int64 up = 0, down = 0, upd = 0,  downd = 0, dupdown;  
              
            //枚举循环部分的起始位置  
            for(int t = spos; t <= epos; t++)  
            {  
                upd = up = 0;  
                int k = spos;  
                while(k < t && input[k] == '0') k++; //找到非循环部分第一个不为0的位置  
                for(; k <= epos; k++)  
                {  
                    if(k < t) upd = upd * 10 + int(input[k] - '0');   //统计非循环部分的分子值  
                    if(k >= t) up = up * 10 + int(input[k] - '0');     //统计循环部分的分子值  
                }  
                  
                downd = pow(10.0, t - spos);                            //非循环部分的分母值  
                down = pow(10.0, epos - spos + 1);                 //所有小数部分的分母值  
                dupdown = pow(10.0, epos - t + 1);                 //循环部分的分母值  
                down = down - down / dupdown;                     //制造循环部分小数的最终分母值  
                if(up != 0) trim(up, down);                                 //约分  
                if(upd != 0) trim(upd, downd);                           //约分  
                  
                __int64 newup, newdown;  
                newup = up * downd + down * upd;  
                newdown = down * downd;  
                if(newup != 0) trim(newup, newdown);            //计算最终结果  
                  
                if(mindown == -1 || newdown < mindown)       //取分母最小的  
                {  
                    mindown = newdown;  
                    minup = newup;  
                }  
            }  
            printf("%I64d/%I64d/n", minup, mindown);  
        }  
        //全为0的情况  
        else printf("0/1/n");  
    }  
    return 0;  
}  




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值