1. 程式人生 > >算法習題---3.08循環小數(UVa202)

算法習題---3.08循環小數(UVa202)

是否 兩個 是不是 一位 輸出 mem ++ void pri

一:題目

輸入整數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);
}

算法習題---3.08循環小數(UVa202)