题解 P1530 【分数化小数 Fractions to Decimals】

· · 题解

[USACO2.4]Fractions to Decimals【分数化小数】

PS:一道经典的模拟题(或许以后高精会用上),需要用到数学的基本知识(如何判断循环节)

题意简述

给你分数的分子 N 和分母 D ,求 \frac{N}{D} 的分值。如果分值是整数,保留一位小数。如果分值是循环小数,用小括号 () 括住循环节。如果答案长度超过 76 ,每行输出 76 个字符后换行。

题目分析

注意:N/D是分数,分数属于有理数,有理数分为整数、有限小数和无限循环小数

这道题让你算的 \frac{N}{D} 的值会有多种情况:

算法

模拟

算法解析

由于用double会爆精,我们只好进行手动的除法模拟。经过 \color{purple}RE 的手动验证,我发现有些答案的长度超过了10^{7} ,所以不能用数组模拟,因为int类型数组开10^{8}会爆空间 (MLE) 。所以我们得用字符串模拟除法(因为字符串是无限空间的)。

程序

备注:do{}while()是先执行一次{}里的程序后,再进行判断符不符合()里的条件而决定是否继续循环的循环语句。
#include<iostream>
#include<fstream>
#include<cstdio>
#include<algorithm> 
#include<string>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
int n,d,k,w,sum,len;
//w用来记录循环节开始(左括号)的位置,sum为输出使用的计数器 
string ans;
//答案 
int pd[100005];
//pd数组用来记录每个余数出现的位置,判断余数是否重复,便于记录循环节开始(左括号)的位置。根据题目1<=N,D<=100000可得知余数最终不会超过99999 
int main()
{

    scanf("%d%d",&n,&d);
    k=n/d,w=-1;
    do
    {
        ans=char(k%10+'0')+ans;
        //这种方法要把拆分出来的整数放到前面去,不然会反过来,因为它先拆分个位,再拆分十位,直接加会使个位在前,直接加会使十位在后, 
        k/=10;
    }while(k>0);
    //拆分结果的整数部分进ans。由于0也算是整数部分,所以无论如何也要拆分一次,用do{}while(),不然用for() 整数部分为0时就不会拆进去,因为k=0了 
    ans+='.';
    //鉴于结果无论如何至少有一位小数,加个小数点
    n=n%d;
    //被除数取余,开始模拟小数部分的除法运算 
    do
    {
        if(pd[n]!=0)
        {
            w=pd[n];
            break;
        }
        //如果余数有重复,说明这是一个循环小数,上一个n出现的位置便是循环节开始(左括号)的位置
        else pd[n]=ans.size();
        //否则就记下这个余数变为被除数后对应的商出现的位置,以便未来判断和记录 
        n*=10;
        //n变成被除数,末尾加0(N,D一定是正整数,不存在小数可加在末尾)
        k=n/d;
        ans+=char(k+'0');
        n=n%d;
        //模拟除法 
    }while(n!=0);
    //由于结果如果是正整数的话,末尾也得加0,用do()while{} 
    if(w!=-1) ans+=')';
    //如果结果是一个循环小数,在末尾加个右括号,便于输出 
    len=ans.size();
    for(int i=0;i<len;i++)
    {
        if(i==w)
        {
            putchar('(');
            i--;
            w=-1;
        }
        //如果这个地方是循环节的开头,在这里输出左括号,并将w改为-1,避免重复输出括号,同时i--,因为这里的ans[i]还没输出  
        else putchar(ans[i]);
        sum++;
        if(sum%76==0) putchar('\n');
        //putchar( )可理解为cout<<char( );
    }
    //注:我这样处理循环节的开头是为了便于换行,因为左括号也算是字符 
    return 0;
}

宣传一下我的博客(给写了一天的我点个赞吧%%%)。 \color{orange} \text{欢迎私信给予您宝贵的意见,让我的题解做得更好,有错误敬请私信我} @童年如作业 \color{orange} \text{,我将作出更正!}

\color{red} \text{谢谢观赏!!!}