POJ 1207 3n+1問題

描述

考慮下列算法:

 1. input n
 2. print n
 3. if n = 1 then STOP
 4.     if n is odd then n <- 3n+1
 5.     else n <- n/2
 6. GOTO 2

輸入:22,
打印序列:22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

假設對於所有整數值,算法可終止(打印到1時),但不知道假設是否爲真。對於所有的整數n,例如0<n<1,000,000 是已驗證的。

給定輸入n,可能確定在打印到1時一共輸出的數字的個數,該個數成爲n的週期長度,對於上面的例子,22的週期長度爲16.

對於任意兩個數i,j,找到i,j之間的所有整數中,最大的週期長度。

輸入

輸入是一系列整數對i,j,每行一對整數。所有整數n,0<n<10,000 ,需要測試[i,j]內的所有整數,來確定最大的週期長度

輸出

對於每對輸入整數i,j,需要輸出i,j和[i,j]區間內的最大的週期長度。這三個數用至少一個空格分開,位於一行內。輸出的i,j要和輸入的順序相同。

輸入樣例

1 10
100 200
201 210
900 1000

輸出樣例

1 10 20
100 200 125
201 210 89
900 1000 174

思路

直接計算的話,由於數據計算量在10,000內,可以
打表的話,與直接計算只能節省重複區間的計算量,不會快很多
最快的算法是深度搜索+記憶優化(記憶優化相當於打表),適用於n爲更大的數時,作爲拓展瞭解一下吧。。。
注意:
1. 打印1本身也算在週期內
2. 每對數i,j不一定是i< j,需要格外留意,輸出時要按照輸入時的順序輸出

1 直接計算

164K 16MS C 859B

#include <stdio.h>

int steps(n)
{
    int count = 1;
    while(n != 1)
    {
        if(n&1)//按位與運算,奇數的二進制末尾爲1,奇數&1結果爲1
        {
            //設 k = n/2, 則 3(2k+1)+1 = (3k+2)*2,減少一次迭代
            n = n/2 * 3 + 2;
            count += 2;
        }
        else
        {
            n /= 2;
            ++count;
        }
    }
    return count;
}

int main()
{
    int i,j,max,t,k,k_step;//t用於交換,k用於遍歷
    int tmp_i,tmp_j;//用於保存輸入的i,j的順序
    while(scanf("%d%d",&i,&j)==2)
    {
        tmp_i = i;
        tmp_j = j;
        if(i>j){
            t = i;
            i = j;
            j = t;
        }
        max = steps(i);
        for(k = i+1; k <= j; ++k)
        {
            k_step = steps(k);
            if(max < k_step)
                max = k_step;
        }
        printf("%d %d %d\n", tmp_i,tmp_j,max);
    }
    return 0;
}

2 打表

204K 0MS C 924B

#include <stdio.h>

int steps_arr[10001] = {0};//1開始計數
void calcSteps()
{
    int i,n;
    for(i=1; i < 10001; ++i)
    {
        steps_arr[i] = 1;
        n = i;
        while(n!=1)
        {
            if(n&1){
                n = n/2 * 3 + 2;
                steps_arr[i] = steps_arr[i] + 2;
            }
            else
            {
                n /= 2;
                steps_arr[i] = steps_arr[i] + 1;
            }

        }
    }
}

int main()
{
    int i,j,t,k,max;
    int temp_i,temp_j;
    calcSteps();
    while(scanf("%d%d",&i,&j)==2)
    {
        temp_i = i;
        temp_j = j;
        if(i > j)
        {
            t = i;
            i = j;
            j = t;
        }
        max = steps_arr[i];
        for(k = i+1; k <= j; ++k)
        {
            if(max < steps_arr[k])
                max = steps_arr[k];
        }
        printf("%d %d %d\n",temp_i, temp_j, max);
    }
    return 0;
}

深度搜索+記憶優化

244K 0MS C 941B

#include <stdio.h>
int steps_arr[20001] = {0};
int dfs(int n)
{
    if(n==1)
        return 1;
    if(n > 20000)
    {
        if(n&1)
            return dfs(n/2 *3 + 2) + 2; //這裏就是深度搜索,向下遞歸一層
        else
            return dfs(n/2) + 1;
    }
    if(!steps_arr[n]) //沒有求過n的步數,則需要求(否則可以用上次返回的值【記憶】)
    {
        if(n&1)
            return dfs(n/2 *3 + 2) + 2;
        else
            return dfs(n/2) + 1;
    }
    else
        return steps_arr[n];
}

int main()
{
    int i,j,max,k,t;
    steps_arr[1] = 1;
    for(i = 1; i < 10001; ++i)
    {
        steps_arr[i] = dfs(i);//打表,主要保存那些頻繁計算的數的步數
    }
    while(scanf("%d%d",&i,&j)==2)
    {
        printf("%d %d ",i,j);
        max = 0;
        if(i > j)
        {
            t = i;
            i = j;
            j = t;
        }
        for(k = i; k <= j; ++k)
        {
            if(max < steps_arr[k])
                max = steps_arr[k];
        }
        printf("%d\n", max);
    }
    return 0;
}

參考

  1. http://www.cnblogs.com/yinger/archive/2011/07/15/2107761.html
  2. http://www.cnblogs.com/CocoonFan/archive/2013/03/02/2940336.html
  3. https://github.com/YvesChan/codebak/blob/master/POJ%201207%20模擬水過,內有對比程序.cpp
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章