HDOJ 1058 Humble Numbers解题报告【DP】

  
  
  
  
Humble Numbers
题目详见http://acm.hdu.edu.cn/showproblem.php?pid=1058
 开始拿到这个题目的时候还纠结了半天,英语很差的话这个题是不可能AC的。。而我就是其中之一。。。
 Humber Number不用管它啥意思,就是一类定义的数而已。如果一个数的质因数(素因数)仅仅是2、3、5 or 7的话那就被称为Humber Number。特殊的1也在其列。而且题目给出了前20个Humber Number。1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 25, 27。注意仅仅二字,11包含质因数11,26包含质因数13都不在其列。编写程序求第n个Humble Number。输入输出格式要搞明白。
 OK,搞清了题意,那就来分析一下。这个题的意思很清楚明了,就是给你一个N,求出第N个Humble Number。如何求?一开始我想到遍历,可是觉得很麻烦,估计会很超时。我就想啊,既然是2 3 5 7 ,我用1和他们相加如何?得到了3 4 6 8,这些数肯定是Humble Number。可是如何继续下去呢?用3和2 3 5 7相加?得到 5 6 8 10,还要用4和他们相加?继续下去?感觉很繁琐,就像是一颗树4叉树一样。  
 所以我放弃了这个思路,又开始想遍历,如果我知道了某个数是不是Humble Number,那我不就可以统计第N个了吗?从1开始遍历,如果i是Humble Number,那我就nums++,知道nums和你给定的N相等我们就求出了。。如何判断一个数是不是Humble Number呢?Humble Number的质因数只能是2 3 5 7 ,被这些数分别整除后结果肯定是1,而且整除次数最多是Number/2.
OK,上代码

#include <iostream>   
using namespace std;

//查看n是不是Humble Number
int IsHumbleNumber(long int n);
    
int main()
{	
	long int nums;
	long int number;
	long int i;
	while(cin>>number)
	{
		if(0==number)
			break;
		nums=0; //初始化  记录有多少个HumbleNumber 
		for(i=1;;i++)
		{
			if(IsHumbleNumber(i))
				nums++;
			if(number==nums)
				break;
		} //此时i的值就是第number个humble number
		//输出格式////
		if((number>=4)&&(number<=20))
			cout<<"The "<<number<<"th humble number is "<<i<<endl; //英语差了这个题别想AC了。。。
		else if(1==(number-(number/10)*10))
			cout<<"The "<<number<<"st humble number is "<<i<<endl;
		else if(2==(number-(number/10)*10))
			cout<<"The "<<number<<"nd humble number is "<<i<<endl;
		else if(3==(number-(number/10)*10))
			cout<<"The "<<number<<"rd humble number is "<<i<<endl;
		else
			cout<<"The "<<number<<"th humble number is "<<i<<endl; 
	}
	return 0;
}


//查看n是不是Humble Number
int IsHumbleNumber(long int n)
{
	//long int num=1; 
	if(n==1)  //1 is Humble Number
		return true;
	long int num=n;
	long int i;
	for(i=1;i<=num/2;i++)
	{
		if(n%2==0)
			n/=2;
		else if(n%3==0)
			n/=3;
		else if(n%5==0)
			n/=5;
		else if(n%7==0)
			n/=7;
	}
	if(n==1)
		return true;
	else
		return false;
}

 提交之后发现超时,时间复杂度是Humber(N),这个N如果是5842,那么Humber(N)=20亿,超时是必然的结果。而且每次给定的N,都要从头来过。这让我想到了数组,用一个数组把所有的全部存起来,会不会好一些呢?
代码
#include <iostream>   
using namespace std;

//查看n是不是Humble Number
int IsHumbleNumber(int n);
int main()
{	
	int nums;
	int number;
	long int i;
	int j;
	long int humble[5843]={0};//0位不用
	for(j=1;j<=5842;j++)
	{
		for(i=humble[j-1]+1;;i++)
		{
			if(IsHumbleNumber(i))
				break;
		} 
		humble[j]=i;//此时i的值就是第j个humble number
	}
	while(cin>>number)
	{
		if(0==number)
			break;

		//输出格式////
		if((number>=4)&&(number<=20))
			cout<<"The "<<number<<"th humble number is "<<humble[number]<<endl; //英语差了这个题别想AC了。。。
		else if(1==(number-(number/10)*10))
			cout<<"The "<<number<<"st humble number is "<<humble[number]<<endl;
		else if(2==(number-(number/10)*10))
			cout<<"The "<<number<<"nd humble number is "<<humble[number]<<endl;
		else if(3==(number-(number/10)*10))
			cout<<"The "<<number<<"rd humble number is "<<humble[number]<<endl;
		else
			cout<<"The "<<number<<"th humble number is "<<humble[number]<<endl; 
	}
	return 0;
}


//查看n是不是Humble Number
int IsHumbleNumber(int n)
{
	//long int num=1; 
	if(n==1)  //1 is Humble Number
		return true;
	int num=n;
	int i;
	for(i=1;i<=num/2;i++)
	{
		if(n%2==0)
			n/=2;
		else if(n%3==0)
			n/=3;
		else if(n%5==0)
			n/=5;
		else if(n%7==0)
			n/=7;
	}
	if(n==1)
		return true;
	else
		return false;
}
 NO,还是超时!因为整个求Humber Number的过程是Humber(5842),也就是20亿,肯定会超时啦。。。所以来说,这个是行不通的。前100个还是很好求的,后面就不行了。
 逼不得已放弃这个思路。看这个20亿,我觉得时间复杂度可不可以是和N相关的和Humber Number没关系,O(N)?可以不?
 想一想开始时候的想法,4叉树,必然有很多相同的节点,那么这就是重叠子问题?而且求第N个Humber Number是和第N-1个Humber Number有关系的?也就是说问题的最优解可以用子问题的最优解来解决,这不是传说中的DP吗?
 OK,有点兴奋,怎么做?如何得到表达式?用第一次的思路,用加不行,可以用乘吗?1*2,1*3,1*5,1*7?得出的结果怎么办?所有的数还要和 2 3  5 7相乘,这不是和用加是一样的吗?绞尽脑汁,终于发现了一点规律了,一开始用1相乘的结果中最小值的就是2,这就是第2个Humber Number。然后呢?如果用2和2 3 5 7 相乘得到最小值是4,唉,不是3啊。可是如果2和2相乘 而3 5 7 还是和1相乘,那最小值不就是3了。YES!这是个规律。此时得到了2  3 ,如果用2和2相乘,3和2相乘,5 7 和1相乘,那么最小值就是4,OK,我发现新大陆了。 这个表达式好像就是用2 3 5 7和已经得到的Humber Number相乘啊。
 继续找规律,得到了4,那么如何得到5?3和2相乘,2和3相乘,1和5相乘,1和7相乘最小值就是5。和2 3 5 7 相乘的数就是之前的Humber Number,可是是哪个呢?根据刚才的规律我我们可以判断的是,一开始1,如果这个数和2 3 5 7 中的一个相乘得到的结果是最小值,那么就往后推一个Humber Number,也就是Humber数组的i++。如果有重复的都给他++。
 得到状态转移方程 humble[i]=FMin(humble[p2]*2,humble[p3]*3,humble[p5]*5,humble[p7]*7);
编码

#include <iostream>   
using namespace std;

//求所有的Humble Number
void  HumbleNumber(long int humble[],int n);
long int FMin(long int m,long int n,long int k,long int l);    
int main()
{	
	int nums;
	int number;
	long int humble[5843];
	HumbleNumber(humble,5842);
	while(cin>>number)
	{
		if(0==number)
			break;

		//输出格式////
		if((number>=4)&&(number<=20))
			cout<<"The "<<number<<"th humble number is "<<humble[number]<<"."<<endl; //英语差了这个题别想AC了。。。
		else if(1==(number-(number/10)*10))
			cout<<"The "<<number<<"st humble number is "<<humble[number]<<"."<<endl;
		else if(2==(number-(number/10)*10))
			cout<<"The "<<number<<"nd humble number is "<<humble[number]<<"."<<endl;
		else if(3==(number-(number/10)*10))
			cout<<"The "<<number<<"rd humble number is "<<humble[number]<<"."<<endl;
		else
			cout<<"The "<<number<<"th humble number is "<<humble[number]<<"."<<endl; 
	}
	return 0;
}


//求所有的Humble Number
void  HumbleNumber(long int humble[],int n)
{
	int i;
	int p2,p3,p5,p7;
	p2=p3=p5=p7=1;
	humble[1]=1;
	for(i=2;i<=n;i++)
	{
		humble[i]=FMin(humble[p2]*2,humble[p3]*3,humble[p5]*5,humble[p7]*7);
		if(humble[i]==humble[p2]*2)//不用else if 为了重复的
			p2++;
		if(humble[i]==humble[p3]*3)
			p3++;
		if(humble[i]==humble[p5]*5)
			p5++;
		if(humble[i]==humble[p7]*7)
			p7++;
	}
}

long int FMin(long int m,long int n,long int k,long int l)
{
	long int min1,min2;
	if(m<=n)
		min1=m;
	else
		min1=n;
	if(k<=l)
		min2=k;
	else
		min2=l;
	if(min1<=min2)
		return min1;
	else
		return min2;
}

 时间复杂度是O(N),就是在5842次就可以全部求出了,然后给定的N就可以O(1),得到结果了。很不错的算法。没有超时,nice!可是结果错误,我那个迷茫啊,彷徨啊。。可是仅仅发现输出结果少了一个尾号”.”,我赶紧添上,又是失败,看来不是这个问题,再说这也该是格式错误的提示啊。。
 最后我查了资料才发现,这个英语真心坑啊,看来不是4-20是th,判断的时候不仅仅是最后一位,还和最后两位有关系啊。。真心跪了。。
最后一次提交!!!

#include <iostream>   
using namespace std;

//求所有的Humble Number
void  HumbleNumber(long int humble[],int n);
long int FMin(long int m,long int n,long int k,long int l);    
int main()
{	
	int nums;
	int number;
	long int humble[5843];
	HumbleNumber(humble,5842);
	while(cin>>number)
	{
		if(0==number)
			break;
		//输出格式////
		if((1==number%10)&&(11!=number%100))
			cout<<"The "<<number<<"st humble number is "<<humble[number]<<"."<<endl;
		else if((2==number%10)&&(12!=number%100))
			cout<<"The "<<number<<"nd humble number is "<<humble[number]<<"."<<endl;
		else if((3==number%10)&&(13!=number%100))
			cout<<"The "<<number<<"rd humble number is "<<humble[number]<<"."<<endl;
		else
			cout<<"The "<<number<<"th humble number is "<<humble[number]<<"."<<endl; 
	}
	return 0;
}


//求所有的Humble Number
void  HumbleNumber(long int humble[],int n)
{
	int i;
	int p2,p3,p5,p7;
	p2=p3=p5=p7=1;
	humble[1]=1;
	for(i=2;i<=n;i++)
	{
		humble[i]=FMin(humble[p2]*2,humble[p3]*3,humble[p5]*5,humble[p7]*7);
		if(humble[i]==humble[p2]*2)//不用else if 为了重复的
			p2++;
		if(humble[i]==humble[p3]*3)
			p3++;
		if(humble[i]==humble[p5]*5)
			p5++;
		if(humble[i]==humble[p7]*7)
			p7++;
	}
}

long int FMin(long int m,long int n,long int k,long int l)
{
	long int min1,min2;
	if(m<=n)
		min1=m;
	else
		min1=n;
	if(k<=l)
		min2=k;
	else
		min2=l;
	if(min1<=min2)
		return min1;
	else
		return min2;
}


成功!!鲜红的Accepted,很有成就感啊。
总的来说这个题是不难的,只要用心去想肯定可以AC,这也算是很简单的DP啦。不得不吐槽的是和英语太有关系了。。。
转载请注明出处http://blog.csdn.net/liangbopirates/article/details/9632829




你可能感兴趣的:(算法,动态规划,ACM,hdoj)