• 算法习题---3.08循环小数(UVa202)


    一:题目

    输入整数a和b(0<=a<=3000,1<=b<=3000),输出a/b的循环小数表示以及循环节长度。
    
    例如,a=5,b=43,小数表示为0.(116279069767441860465),循环字节长度为21

    当循环节长度超过50时,后面使用...代替,不进行输出后面部分

    (一)样例输入

    76 25
    5 43
    1 397

    (二)样例输出

    76/25 = 3.04(0)
      1 = number of digits in repeating cycle
    5/43 = 0.(116279069767441860465)
      21 = number of digits in repeating cycle
    1/397 = 0.(00251889168765743073047858942065491183879093198992...)
      99 = number of digits in repeating cycle

    二:题目分析

    (一)余数用作判断循环节的依据:(循环节长度大小不会超过余数大小,原因如下:)

    1/6-->10/6=1...4
    4/6-->40/6=6...4出现两次4,所以一定会出现循环节
    4/6-->40/6=6...4出现三次4,所以循环节就在二三次之间(即商6)
    所以用一个数组记录余数,大小不会超过除数大小

    (二)同时也要记录商,用于记录循环节

    若是除数为P,则余数为0->P-1;所以在P次之间一定会出现两个相同余数  //(一中原因)
    -->一定会出现循环节
    所以我们再记录一次余数,当出现某个余数出现为3次,那么循环节在两个余数位置中间
    由于商数组和余数数组对应,所以可以轻松找到循环节

    (三)商数组和余数数组大小

    余数数组大小应该为除数大小  //余数只需要除数大小,用于记录余数出现次数
    商数组大小应该为除数大小的两倍  //商用于记录对应余数中的商值,由于每一个余数必须出现3次,所以我们记录的商必须足够长,足以支持余数次数到达3

    例:

    其中0<=a<=3000,1<=b<=3000--->
    所以余数数组不会超过3000 商数组大小不超过6000

    注:上面的商和余数数组只记录小数点后的值,不记录整数值

    三:代码分析(使用76/25 ,1/6 ,5/43分析)

    (一)处理整数部分:只剩下会出现小数部分的被除数进行下一步处理

            //开始处理数据
            //1.获取整数
            //先记录整数:可以在记录时直接输出,节省篇幅
            printf("%d/%d = ", a, b);
            if (!(a / b))
                printf("0");
            else
                while (a / b)
                {
                    printf("%d", a / b);
                    a %= b;
                }
            printf(".");

    (1)76/25

    76/25=3...1           
    代码输出76/25 = 3.

    (2)1/6

    直接为小数,所以按照代码输出1/6 = 0.

    (3)5/43

    直接为小数,所以按照代码输出5/43 = 0.

    (二)获取商数组和余数数组

            //2.开始记录商和余数
            i = 0;
            while (1)    //例如:1/6
            {
                //记录商和余数
                res[i] = a * 10 / b;    //10/6=1            //1.记录商
                rem[a * 10 % b]++;        //10%6=4          //2.记录对应余数位置出现次数
    
    //注意两次和三次都是只记录一次即可
                if (rem[a * 10 % b] == 2&&!two_flag)        //3.若是出现两次,则开始进行记录  只记录第一个出现两次的余数
                { 
                    r_s = i;
                    two_flag = 1;
                }
                if (rem[a * 10 % b] == 3)          //4.若是出现3次,则确定了循环节位置,直接跳出
                {
                    r_e = i;
                    break;
                }
                //处理被除数
                a = a * 10 % b;            //a = 4
                i++;
            }

    (1)76/25

        10/25=0...10          0  1  第一个为商下标,第二个为余数次数
        100/25=4...0          1  1  
        0/25=0...0            2  2  余数出现两次时的商0和出现一次时的商4不一致
        0/25=0...0            3  3
    res = { 0 4 0 0}
    rem = { 3 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}

    (2)1/6

        10/6=1...4           0  1  第一个为商下标,第二个为余数次数
        40/6=6...4           1  2  余数出现两次时的商6和出现一次时的商1不一致
        40/6=6...4           2  3
    res = { 1 6 6 }
    rem = { 0 0 0 0 3 0 }

    (3)5/43

        50/43=1...7          0     1
        70/43=1...27
        
        ...
        .....=1...7          21    2  余数出现两次时的商和出现一次时的商一致
        
        ...
        .....=1...7          42    3

    (三):进行数据输出

    注意记录数据时要区分0.1(6) 3.04(0) 和0.(11....)
    不同之处在于循环节是不是从小数第一位开始
    处理原则在于看1,2两次余数相同时,商是否一致(上面案例)
            int rep_count = r_s;
            if (res[r_s] != res[2 * r_s - r_e])  //若是余数第一次和第二次对应的商值不一样,则加一
                rep_count++;
    
            for (j = 0; j <rep_count ;j++)    //注意,先看下面注释,可以知道,真正循环节结束在我们获取的第二个相同余数位置-->即r_s
            {
                if (j == 2 * r_s - r_e && rep_count==r_s)    //注意这里:不是j-r_s
                    printf("(");
                else if (j == 2 * r_s - r_e+1&&rep_count==r_s+1)    //遇到循环节不是从小数点后开始,则向后一位
                    printf("(");
                printf("%d", res[j]);
                if (j +r_e- 2*r_s == 49)    //不是j-r_s    -->因为我们虽然确定r_s到r_e时一个循环节,但是r_s是第二个相同余数开始,r_e是第三个余数开始
                {                            //4 4 4--->r_s是第二个开始,r_e是第三个开始,但是真的循环节是从第一个4到第二个4
                    printf("...");
                    break;
                }
            }
    
            printf(")
    ");
            printf("    %d = number of digits in repeating cycle
    ", r_e - r_s);

    四:代码实现

    void test33()
    {
        int a, b;
        int zs[4];    //存放整数,例如结果10.13161616,这里用于存放10
        //由于程序变更,后面没有用到zs,但是为了便于理解,留在这
        int res[MAX_REP];    //存放商
        int rem[MAX_REP];    //存放余数
    
        int two_flag; //注意两次和三次都是只记录一次即可,但是由于三次直接break,所以不做标志
    
        int i, j;
        int r_s, r_e;    //循环节开始,循环节结束
    
        FILE* fi = freopen("data2.in", "r", stdin);
        freopen("data2.out", "w", stdout);
    
        while (!feof(fi))
        {
            scanf("%d", &a);
            scanf("%d", &b);
    
            memset(zs, 0, sizeof(zs));
            memset(res, 0, sizeof(res));
            memset(rem, 0, sizeof(rem));
    
            two_flag = 0;
    
            //开始处理数据
            //1.获取整数
            //先记录整数:可以在记录时直接输出,节省篇幅
            printf("%d/%d = ", a, b);
            if (!(a / b))
                printf("0");
            else
                while (a / b)
                {
                    printf("%d", a / b);
                    a %= b;
                }
            printf(".");
    
            //2.开始记录商和余数
            i = 0;
            while (1)    //例如:1/6
            {
                //记录商和余数
                res[i] = a * 10 / b;    //10/6=1
                rem[a * 10 % b]++;        //10%6=4
    
                //注意两次和三次都是只记录一次即可
                if (rem[a * 10 % b] == 2&&!two_flag)        //若是出现两次,则开始进行记录
                { 
                    r_s = i;
                    two_flag = 1;
                }
                if (rem[a * 10 % b] == 3)
                {
                    r_e = i;
                    break;
                }
                //处理被除数
                a = a * 10 % b;            //a = 4
                i++;
            }
    
            //开始记录数据
            //注意记录数据时要区分0.1(6) 3.04(0) 和0.(11....)
            //不同之处在于循环节是不是从小数第一位开始
            //处理原则在于看1,2两次余数相同时,商是否一致
            int rep_count = r_s;
            if (res[r_s] != res[2 * r_s - r_e])
                rep_count++;
    
            for (j = 0; j <rep_count ;j++)    //注意,先看下面注释,可以知道,真正循环节结束在我们获取的第二个相同余数位置-->即r_s
            {
                if (j == 2 * r_s - r_e && rep_count==r_s)    //注意这里:不是j-r_s
                    printf("(");
                else if (j == 2 * r_s - r_e+1&&rep_count==r_s+1)    //遇到循环节不是从小数点后开始,则向后一位
                    printf("(");
                printf("%d", res[j]);
                if (j +r_e- 2*r_s == 49)    //不是j-r_s    -->因为我们虽然确定r_s到r_e时一个循环节,但是r_s是第二个相同余数开始,r_e是第三个余数开始
                {                            //4 4 4--->r_s是第二个开始,r_e是第三个开始,但是真的循环节是从第一个4到第二个4
                    printf("...");
                    break;
                }
            }
    
            printf(")
    ");
            printf("    %d = number of digits in repeating cycle
    ", r_e - r_s);
        }
    
        freopen("CON", "r", stdin);
        freopen("CON", "w", stdout);
    }
  • 相关阅读:
    flex>数据绑定、数据列表 小强斋
    上周问题总结与解决方案
    asp.net 实现无限级分类
    发个项目需求大家瞅瞅
    ASP.NET POST模拟登陆
    HttpWebRequest发送数据 post
    asp.net 生成静态页面
    xcode4.1自带SVN配置
    模拟登录
    C#中用HttpWebRequest/HttpWebResponse来发送/接收数据
  • 原文地址:https://www.cnblogs.com/ssyfj/p/10818986.html
Copyright © 2020-2023  润新知