3n+1

// The 3n+1 problem (3n+1 問題)
// PC/UVa IDs: 110101/100, Popularity: A, Success rate: low Level: 1
// Verdict: Accepted
// Submission Date: 2011-05-22
// UVa Run Time: 0.032s
//
// 版權所有(C)2011,邱秋。metaphysis # yeah dot net。
//
// [問題描述]
// 考慮如下的序列生成算法:從整數 n 開始,如果 n 是偶數,把它除以 2;如果 n 是奇數,把它乘 3 加
// 1。用新得到的值重複上述步驟,直到 n = 1 時停止。例如,n = 22 時該算法生成的序列是:
//
// 22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1
//
// 人們猜想(沒有得到證明)對於任意整數 n,該算法總能終止於 n = 1。這個猜想對於至少 1 000 000
// 內的整數都是正確的。
//
// 對於給定的 n,該序列的元素(包括 1)個數被稱爲 n 的循環節長度。在上述例子中,22 的循環節長度
// 爲 16。輸入兩個數 i 和 j,你的任務是計算 i 到 j(包含 i 和 j)之間的整數中,循環節長度的最大
// 值。
//
// [輸入]
// 輸入每行包含兩個整數 i 和 j。所有整數大於 0,小於 1 000 000。
//
// [輸出]
// 對於每對整數 i 和 j,按原來的順序輸出 i 和 j,然後輸出二者之間的整數中的最大循環節長度。這三
// 個整數應該用單個空格隔開,且在同一行輸出。對於讀入的每一組數據,在輸出中應位於單獨的一行。
//
// [樣例輸入]
// 1 10
// 100 200
// 201 210
// 900 1000
//
// [樣例輸出]
// 1 10 20
// 100 200 125
// 201 210 89
// 900 1000 174
//
// [解題方法]
// 計算每個數的循環節長度,求給定區間的最大值。
//
// 需要注意:
// 1. 中間計算過程會超過 int 或 long (如果 int 或 long 型均爲 4 字節存儲空間) 型數據所能
//    表示的範圍,故需要選擇 long long (8 字節存儲空間)型整數(除非你使用的算法在做乘的時候不
//    使用一般的乘法,而是使用替代方法實現原數的三倍加一)。
// 2. 輸入時可能較大的數在前面,需要調整順序,這個是導致算法正確卻 WA 的重要原因。
// 3. 採用填表的方法保存既往計算結果,可以顯著減少計算時間。
//
// 從網絡上看了許多別人的解題方案,大多數都是忽略了第一點,求循環節長度的過程中,選擇了 int 或
// long (按 32 位 CPU 來假定,4 字節存儲空間)類型的數據,當計算 (n * 3 + 1) 時會超出 32
// 位整數的表示範圍而得到錯誤答案,只不過 Programming Challenges 和 UVa 上的測試數據不是很強,
// 所以儘管不完善但都會獲得 AC。在 1 - 999999 之間共有 41 個數在中間計算過程中會得到大於 32 位
// 無符號整數表示範圍的整數,當測試數據包含這些數時,選用 int 或 long 類型有可能會得到錯誤的答案。
//
// 在中間計算過程中會超過 32 位整數表示範圍的整數(括號內爲循環節長度):
// 159487(184)  270271(407)  318975(185)  376831(330)  419839(162)
// 420351(242)  459759(214)  626331(509)  655359(292)  656415(292)
// 665215(442)  687871(380)  704511(243)  704623(504)  717695(181)
// 730559(380)  736447(194)  747291(248)  753663(331)  763675(318)
// 780391(331)  807407(176)  822139(344)  829087(194)  833775(357)
// 839679(163)  840703(243)  847871(326)  859135(313)  901119(251)
// 906175(445)  917161(383)  920559(308)  937599(339)  944639(158)
// 945791(238)  974079(383)  975015(321)  983039(290)  984623(290)
// 997823(440)
      
#include <iostream>
      
using namespace std;
#define min(a, b) ((a) <= (b) ? (a) : (b))
#define max(a, b) ((a) >= (b) ? (a) : (b))
#define MAXSIZE 1000000
      
int cache[MAXSIZE];
// 計算循環節長度。
int counter(long long number)
{
    if (number == 1)
        return 1;
      
    // 模 2 計算可用與計算代替,除 2 計算可用右移計算代替。
    if (number & 1)
        number += (number << 1) + 1;
    else
        number >>= 1;
      
    // 若 number 在緩存範圍內則根據情況取用。
    if (number < MAXSIZE )
    {
        if (!cache[number])
            cache[number] = counter(number);
        return 1 + cache[number];
    }
      
    return 1 + counter(number);
}
      
int main(int ac, char *av[])
{
    // 對於 GUN C++ 編譯器,使用默認參數,在編譯時會自動將全局數組 cache 中未初始化
    // 的元素初始化爲 0,故可以不需要顯式的進行初始化的工作。對於其他編譯器應該根據情況調整。
    //
    // memset(cache, 0, sizeof(cache));
    //
    int first, second, start, end;
    while (cin >> first >> second)
    {
        // 得到給定範圍的上下界。
        start = min(first, second);
        end = max(first, second);
          
        // 查找最大步長值。
        int result = 0, steps;
        for (int i = start; i <= end; i++)
            if ((steps = counter(i)) > result)
                result = steps;
        // 輸出。
        cout << first << " " << second << " " << result << endl;
    }
      
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章