算法习题---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(")\n");
        printf("    %d = number of digits in repeating cycle\n", 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(")\n");
        printf("    %d = number of digits in repeating cycle\n", r_e - r_s);
    }

    freopen("CON", "r", stdin);
    freopen("CON", "w", stdout);
}

 

posted @ 2019-05-06 13:06  山上有风景  阅读(726)  评论(0编辑  收藏  举报