The 3n + 1 problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 38483 Accepted Submission(s): 13999
题目链接:
Consider the following algorithm:
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
Given the input 22, the following sequence of numbers will be printed 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
It is conjectured that the algorithm above will terminate (when a 1 is printed) for any integral input value. Despite the simplicity of the algorithm, it is unknown whether this conjecture is true. It has been verified, however, for all integers n such that 0 < n < 1,000,000 (and, in fact, for many more numbers than this.)
Given an input n, it is possible to determine the number of numbers printed (including the 1). For a given n this is called the cycle-length of n. In the example above, the cycle length of 22 is 16.
For any two numbers i and j you are to determine the maximum cycle length over all numbers between i and j.
You should process all pairs of integers and for each pair determine the maximum cycle length over all integers between and including i and j.
You can assume that no opperation overflows a 32-bit integer.
1 10 100 200 201 210 900 1000
1 10 20 100 200 125 201 210 89 900 1000 174
感觉题目特别坑,给出的n最大是1000000,按照平时在杭电做题的套路,想着如果暴力去求解,那么肯定会超时啊,所以就想这肯定打表,然后发现像题目中给的数据: 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
如果以后在求一个数n的3*n+1问题的长度,在遇到上面这些数字,就不用再往下求解了。直接由当前数字的3*n+1长度加上n到这个数字的循环次数得到的就是一个数n的3*n+1长度,还有一点就是,如果求出了2的3*n+1长度,那么
2 4 8 16 32 ........长度都能求出。
如果求出了3的长度,那么 6 12 24 48 96 ..........长度都能求出
同理如果求出了数n的长度,那么 2*n 2*2*n 2*2*2*n .... n*2^x 的长度都能求出。
而且每次都是乘2乘2的增长,其时间复杂度为对数阶。
但是如果当前遇到一个数n没有求出长度,则先求它的长度, 求长度的时候要注意,因为3*n+1可能超过数组的下标,当超过数组下标的时候,
直接进行计算如果是偶数按偶数的规则操作,奇数按奇数的规则操作。如果没有超过数组下标,如果当前到达的数字对应的长度有值了,则可以得到答案,否则,根据奇偶进行操作。
打表的基本思路关键就是:
1.要利用已经求出的结果。
2.如果一个数n已经求出结果。则len(2*n) = len(n)+1,因为2*n一定是偶数,n一定是2*n经过除2操作得来的,因此2*n的长度是
n的循环长度加1。
最后提交过了,但是杭电特别坑,这个题目后面测试数据弱。我去讨论区看了看竟然普通暴力可以过,所以说题目特别坑爹,让我纠结那么长时间而且特别害怕打表会超时,让我纠结了一晚上,想着如果后面测试数据给出 1 1000000 给100次,那暴力方法,此次都要去求值,根本就不行。不过事实证明自己想多了。下面给出2中方法,方法1:打表,方法2:暴力。
1.打表:
#include<iostream> #include<string.h> #include<stdio.h> #include<set> #include<map> #include<string> #include<stack> #include<queue> #include<algorithm> using namespace std; int a[2000003]; void CreateTable() //这个函数里面有些内容完全不必要,只要a[1],a[2]有值就行。 { memset(a,0,sizeof(a)); ///将数字a初始化为0,代表都没有求出循环长度 a[1] = 1; a[2] = 2; a[3] = 8; a[4] = 3; a[5] = 6; a[6] = 9; a[7] = 17; a[8] = 4; a[9] = 20; a[10] = 7; } int fun(long long int n) ///注意n要用long long,因为3*n+1后数可能很大 { int m = (int)n; int len = 0; //用来存放n的循环长度 while(n != 1) //n不等于1的时候执行循环 { if(n <= 1000000) //如果n没有超过数组的下标 { if(a[n]==0) //没有求出长度 { len++; //长度加1 if(n%2 == 0) n = n/2; else n = n*3+1; } else //代表a[n]之前已经求出,则直接得出答案即可。降低时间复杂度 { a[m] = a[n] + len; break; } } else //越界了,不能再用数组下标去判断一个数是否已求出长度,老老实实计算就好 { len++; if(n%2 == 0) n = n/2; else n = n*3+1; } } return a[m]; } int main() { int start,eend,temp; CreateTable(); for(int i = 2; i <= 500000; i++) ///打表 { if(a[i]!=0) { //如果数i已经求出循环长度,则 i*2^x也都能求出长度,要注意其实按指数方式增长的 //因此其时间复杂度为对数阶,内层循环最多执行十几次,不是很耗时的。 for(int j = 2*i ; j <= 1000000; j=j*2) { if(a[j]==0) ///可能会重复求,所以加判断,实际上判断加与不加没差别的。 { a[j] = a[j/2]+1; } } } else //否则求数i的3*n+1问题的长度 { a[i] = fun(i); i--; //i--,原来i没求出,现在i求出结果了,让i保持不变,为了让去求i*2^n的长度。 } //printf("a[%d] = %d\n",i,a[i]); } while(~scanf("%d%d",&start,&eend)) { printf("%d %d",start,eend); if(start > eend) //小陷阱,start不一定小于eend { temp = start; start = eend; eend = temp; } int mmax = -1; for(int i = start; i <= eend; i++) { if(a[i] > mmax) mmax = a[i]; } printf(" %d\n",mmax); } return 0; }
2.暴力:
#include<iostream> #include<string.h> #include<stdio.h> #include<set> #include<map> #include<string> #include<stack> #include<queue> #include<algorithm> using namespace std; int len(int n) { int num = 1; while(n != 1) { num++; if(n%2 == 0) n = n/2; else n = 3*n + 1; } return num; } int main() { int start,eend,temp; while(~scanf("%d%d",&start,&eend)) { printf("%d %d",start,eend); if(start > eend) { temp = start; start = eend; eend = temp; } int mmax = 1; for(int i = start; i <= eend; i++) { int length = len(i); if(length > mmax) mmax = length; } printf(" %d\n",mmax); } return 0; }