1 // The 3n+1 problem (3n+1 问题) 2 // PC/UVa IDs: 110101/100, Popularity: A, Success rate: low Level: 1 3 // Verdict: Accepted 4 // Submission Date: 2011-05-22 5 // UVa Run Time: 0.032s 6 // 7 // 版权所有(C)2011,邱秋。metaphysis # yeah dot net。 8 // 9 // [问题描述] 10 // 考虑如下的序列生成算法:从整数 n 开始,如果 n 是偶数,把它除以 2;如果 n 是奇数,把它乘 3 加 11 // 1。用新得到的值重复上述步骤,直到 n = 1 时停止。例如,n = 22 时该算法生成的序列是: 12 // 13 // 22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1 14 // 15 // 人们猜想(没有得到证明)对于任意整数 n,该算法总能终止于 n = 1。这个猜想对于至少 1 000 000 16 // 内的整数都是正确的。 17 // 18 // 对于给定的 n,该序列的元素(包括 1)个数被称为 n 的循环节长度。在上述例子中,22 的循环节长度 19 // 为 16。输入两个数 i 和 j,你的任务是计算 i 到 j(包含 i 和 j)之间的整数中,循环节长度的最大 20 // 值。 21 // 22 // [输入] 23 // 输入每行包含两个整数 i 和 j。所有整数大于 0,小于 1 000 000。 24 // 25 // [输出] 26 // 对于每对整数 i 和 j,按原来的顺序输出 i 和 j,然后输出二者之间的整数中的最大循环节长度。这三 27 // 个整数应该用单个空格隔开,且在同一行输出。对于读入的每一组数据,在输出中应位于单独的一行。 28 // 29 // [样例输入] 30 // 1 10 31 // 100 200 32 // 201 210 33 // 900 1000 34 // 35 // [样例输出] 36 // 1 10 20 37 // 100 200 125 38 // 201 210 89 39 // 900 1000 174 40 // 41 // [解题方法] 42 // 计算每个数的循环节长度,求给定区间的最大值。 43 // 44 // 需要注意: 45 // 1. 中间计算过程会超过 int 或 long (如果 int 或 long 型均为 4 字节存储空间) 型数据所能 46 // 表示的范围,故需要选择 long long (8 字节存储空间)型整数(除非你使用的算法在做乘的时候不 47 // 使用一般的乘法,而是使用替代方法实现原数的三倍加一)。 48 // 2. 输入时可能较大的数在前面,需要调整顺序,这个是导致算法正确却 WA 的重要原因。 49 // 3. 采用填表的方法保存既往计算结果,可以显著减少计算时间。 50 // 51 // 从网络上看了许多别人的解题方案,大多数都是忽略了第一点,求循环节长度的过程中,选择了 int 或 52 // long (按 32 位 CPU 来假定,4 字节存储空间)类型的数据,当计算 (n * 3 + 1) 时会超出 32 53 // 位整数的表示范围而得到错误答案,只不过 Programming Challenges 和 UVa 上的测试数据不是很强, 54 // 所以尽管不完善但都会获得 AC。在 1 - 999999 之间共有 41 个数在中间计算过程中会得到大于 32 位 55 // 无符号整数表示范围的整数,当测试数据包含这些数时,选用 int 或 long 类型有可能会得到错误的答案。 56 // 57 // 在中间计算过程中会超过 32 位整数表示范围的整数(括号内为循环节长度): 58 // 159487(184) 270271(407) 318975(185) 376831(330) 419839(162) 59 // 420351(242) 459759(214) 626331(509) 655359(292) 656415(292) 60 // 665215(442) 687871(380) 704511(243) 704623(504) 717695(181) 61 // 730559(380) 736447(194) 747291(248) 753663(331) 763675(318) 62 // 780391(331) 807407(176) 822139(344) 829087(194) 833775(357) 63 // 839679(163) 840703(243) 847871(326) 859135(313) 901119(251) 64 // 906175(445) 917161(383) 920559(308) 937599(339) 944639(158) 65 // 945791(238) 974079(383) 975015(321) 983039(290) 984623(290) 66 // 997823(440) 67 68 #include <iostream> 69 70 using namespace std; 71 72 #define min(a, b) ((a) <= (b) ? (a) : (b)) 73 #define max(a, b) ((a) >= (b) ? (a) : (b)) 74 75 #define MAXSIZE 1000000 76 77 int cache[MAXSIZE]; 78 79 // 计算循环节长度。 80 int counter(long long number) 81 { 82 if (number == 1) 83 return 1; 84 85 // 模 2 计算可用与计算代替,除 2 计算可用右移计算代替。 86 if (number & 1) 87 number += (number << 1) + 1; 88 else 89 number >>= 1; 90 91 // 若 number 在缓存范围内则根据情况取用。 92 if (number < MAXSIZE ) 93 { 94 if (!cache[number]) 95 cache[number] = counter(number); 96 return 1 + cache[number]; 97 } 98 99 return 1 + counter(number); 100 } 101 102 int main(int ac, char *av[]) 103 { 104 // 对于 GUN C++ 编译器,使用默认参数,在编译时会自动将全局数组 cache 中未初始化 105 // 的元素初始化为 0,故可以不需要显式的进行初始化的工作。对于其他编译器应该根据情况调整。 106 // 107 // memset(cache, 0, sizeof(cache)); 108 // 109 int first, second, start, end; 110 111 while (cin >> first >> second) 112 { 113 // 得到给定范围的上下界。 114 start = min(first, second); 115 end = max(first, second); 116 117 // 查找最大步长值。 118 int result = 0, steps; 119 for (int i = start; i <= end; i++) 120 if ((steps = counter(i)) > result) 121 result = steps; 122 123 // 输出。 124 cout << first << " " << second << " " << result << endl; 125 } 126 127 return 0; 128 }
来自csdn博客,作者寂静山林,链接:http://blog.csdn.net/metaphysis/article/details/6431937