HDU:1032 The 3n + 1 problem

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

题目链接:
Problem Description
Problems in Computer Science are often classified as belonging to a certain class of problems (e.g., NP, Unsolvable, Recursive). In this problem you will be analyzing a property of an algorithm whose classification is not known for all possible inputs.

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. 
 

Input
The input will consist of a series of pairs of integers i and j, one pair of integers per line. All integers will be less than 1,000,000 and greater than 0. 

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.
 

Output
For each pair of input integers i and j you should output i, j, and the maximum cycle length for integers between and including i and j. These three numbers should be separated by at least one space with all three numbers on one line and with one line of output for each line of input. The integers i and j must appear in the output in the same order in which they appeared in the input and should be followed by the maximum cycle length (on the same line). 
 

Sample Input
  
  
1 10 100 200 201 210 900 1000
 

Sample Output
  
  
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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值