// 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; }
3n+1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.