Bootstrap

例题分享——2/3/5抽数

题目

把只包含质因子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=2i3j5k
其中肯定是k的系数要小的多, 通过死算,逐步逼近范围,第100个数,逼近范围还是可以算出来的,不要小瞧这种计算,原子弹都能计算这个肯定也能计算就看你愿不愿计算。当然数量过大就还是不要计算吧,笔者以前在竞赛时就计算过,计算的速度比写代码要快,当然当时写代码菜的一塌糊涂。

方法二:类似递归的方法——下标迁移法
只能是类似递归,并不是真正意义上的递归,也就是有点意思,但是不是,不说废话,直接展开:

  1. 分析一个就是上面的那个公式一样,每一个数都是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{2f(n)3f(n),5f(n)} 是不是很像递归,但是抱歉,并不是递归。因为并不好写递归的方法,如果有大牛看到了,写出来了,不要忘记发一下,学习学习。注意:这里分f(n)并不是只是前一个元素,是之前的状态量!状态量!状态量!啥是状态量?就是一个集合。
  2. 设置每个量*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{2Reslut[M2],3Reslut[M3],5Reslut[M5]}一开始都指向下标为0的元素,第一个非零元素2进入开始,下标就开始不一样了,可以写一下
  3. 每一次取最小值,然后更新下标了,为什么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;
}

参考资料:
https://blog.csdn.net/pomay/article/details/74457006

;