题目
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路
主要就是利用类似于递归的思想,或者说是分解+法的方法。
方法一:
每一个丑分解之后只有2/3/5,假设一个数是x,有
x
=
2
i
⋅
3
j
⋅
5
k
x=2^i \cdot 3^j \cdot 5^k
x=2i⋅3j⋅5k
其中肯定是k的系数要小的多, 通过死算,逐步逼近范围,第100个数,逼近范围还是可以算出来的,不要小瞧这种计算,原子弹都能计算这个肯定也能计算就看你愿不愿计算。当然数量过大就还是不要计算吧,笔者以前在竞赛时就计算过,计算的速度比写代码要快,当然当时写代码菜的一塌糊涂。
方法二:类似递归的方法——下标迁移法
只能是类似递归,并不是真正意义上的递归,也就是有点意思,但是不是,不说废话,直接展开:
- 分析一个就是上面的那个公式一样,每一个数都是2/3/5的倍数,也就是说第一个数是1,第二个数,是之前的数每个*2 、*3、 *5然后选择最小的那个,当做新元素。 f ( n + 1 ) = m i n { 2 ∗ f ( n ) , 3 ∗ f ( n ) , 5 ∗ f ( n ) } f(n+1)=min\{2*f(n),3*f(n),5*f(n)\} f(n+1)=min{2∗f(n),3∗f(n),5∗f(n)} 是不是很像递归,但是抱歉,并不是递归。因为并不好写递归的方法,如果有大牛看到了,写出来了,不要忘记发一下,学习学习。注意:这里分f(n)并不是只是前一个元素,是之前的状态量!状态量!状态量!啥是状态量?就是一个集合。
- 设置每个量*2 *3 4的一个表示量,也就是代码里面的int M2 ,int M3 ,int M5 表示数组下标,就是下标为M2的这个数是现在这个2的数。 f ( n + 1 ) = m i n { 2 ∗ R e s l u t [ M 2 ] , 3 ∗ R e s l u t [ M 3 ] , 5 ∗ R e s l u t [ M 5 ] } f(n+1)=min\{2*Reslut[M2],3*Reslut[M3],5*Reslut[M5]\} f(n+1)=min{2∗Reslut[M2],3∗Reslut[M3],5∗Reslut[M5]}一开始都指向下标为0的元素,第一个非零元素2进入开始,下标就开始不一样了,可以写一下
- 每一次取最小值,然后更新下标了,为什么3个if就是因为三个条件的才能选择合适的更新的方式,有重复的情况,都更新一下,躲避数组加入重复数字,如果不重复,不会都满足情况。
下面上代码
#include<iostream>
#include<math.h>
using namespace std;
int GetUglyNumber(int index)
{
if (index <= 0)
return 0;
int* result = new int[index];
result[0] = 1;
int M2 = 0;// 记录最靠近当前数组下标i的,乘以2的元素超过当前元素
int M3 = 0;// 记录最靠近当前数组下标i的,乘以3的元素超过当前元素
int M5 = 0;// 记录最靠近当前数组下标i的,乘以5的元素超过当前元素
for (int i = 1; i < index; i++)
{
// 每一个丑数都是前面丑数乘以2,3或者5得到的
int min_t = min(result[M2] * 2, result[M3] * 3 );
min_t = min(min_t, result[M5] *5);
result[i] = min_t;
//更新移动的下标
if (result[M2] * 2 <= min_t)
M2++;
if (result[M3] * 3 <= min_t)
M3++;
if (result[M5]* 5 <= min_t)
M5++;
}
return result[index - 1];
}
int main()
{
int index;
cin>>index;
cout<<GetUglyNumber(index)<<endl;
return 0;
}