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