问题:Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, …shows the first 11 ugly numbers. By convention, 1 is included. Write a program to find and print the N′th ugly number.
思路:题目要求丑数的因子只能是2、3、5。要我找出从1开始的N个丑数。
方法一:直观的笨办法是,从1开始逐个每个数,看除了2、3、5外还有没有别的因子。效率极低。对于每个自然数,我都要循环除2,循环除3,循环除4,然后看剩下的数是否为1。
方法二:构造丑数表法。由于丑数只是有2、3、5相乘构造出来的,因为我们来分析一下规律。
第一个丑数1,其因子是0个2,0个3,0个5;
第二个丑数2,其因子是1个2,0个3,0个5; 它是在第一个丑数1基础上乘以2得到的;
第三个丑数3,其因子是0个2,1个3,0个5; 它是在第一个丑数1基础上乘以3得到的;
第四个丑数4,其因子是2个2,0个3,0个5; 它是在第二个丑数2基础上乘以2得到的;
第五个丑数5,其因子是0个2,0个3,1个5; 它是在第一个丑数1基础上乘以5得到的;
第六个丑数6,其因子是1个2,1个3,0个5; 它是在第二个丑数2基础上乘以3得到的,同时也是第三个丑数3基础上乘以2得到的;
第七个丑数8,其因子是3个2,0个3,0个5; 它是在第四个丑数4基础上乘以2得到的;
第八个丑数9,其因子是0个2,2个3,0个5; 它是在第三个丑数3基础上乘以3得到的;
第八个丑数10,其因子是1个2,0个3,1个5; 它是在第二个丑数3基础上乘以5得到的;
………………
观察后会发现,每一个丑数都一定正好3次作为之后某丑数的基础,尽管顺序会有所不同。同一个丑数i,会有一次乘2的机会,一次乘3的机会,一次乘5的机会。每个机会一旦用过,就不能再用了。因此,我们设定三个游标i2,i3,i5,用以保存当前乘2机会、乘3机会、乘5机会的使用情况,i2表示当前可以有乘2机会的是第i2个丑数,i3表示当前可以有乘机会的是第i3个丑数,i5表示当前可以有乘5机会的是第i5个丑数。
我首先设置第一个丑数为1,之后,在递推选择下一个丑数的过程中,每次我都会从三个游标所表示的机会如果使用之后所得到的数中选择最小的数,把它任命为下一个丑数,同时使用掉这个机会,即把对应的游标加1,其他的没被选到的机会留作下轮参选。(尤其注意,像6这样的丑数,就同时用掉了2的乘3机会和3的乘2机会)。
代码如下:
#include <stdio.h>
const int N = 1501;
int getmin(int a, int b, int c)
{
if(a <= b)
{
if(a <= c)
return a;
else
return c;
}
else
{
if(b <= c)
return b;
else
return c;
}
}
int find_ugly(int n)
{
int i2=1,i3=1,i5=1;
int ugly[N];
ugly[1] = 1; //ugly[0] not used
int i=2;
int min;
while(i <= n)
{
min = getmin(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5);
ugly[i] = min;
if(min == ugly[i2]*2)
i2++;
if(min == ugly[i3]*3)
i3++;
if(min == ugly[i5]*5)
i5++;
i++;
}
return ugly[n];
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
printf("%d\n", find_ugly(n));
}
return 0;
}

本文介绍了一种高效算法用于查找指定位置的丑数。通过分析丑数的生成规律,提出了利用三个游标来跟踪乘法机会的方法,从而避免了传统逐个检查每个数的低效过程。
549

被折叠的 条评论
为什么被折叠?



