动态规划 求第 n 个 Humble Number(丑数)

13 篇文章 1 订阅
4 篇文章 0 订阅
题目:

 质因数分解是将一个数分解为若干个质数相乘的形式,这些因数可以重复。比如 30 = 2×3×5,20 = 2×2×
5,81 = 3×3×3×3。现在我们将质因数分解之后只出现 2,3,5,7 的数称为 Humble Number,则 20,30,81 均为
Humble Number,而 11,22 不是。前 20 个 Humble Number 的序列为 1,2,3,4,5,6,7,8,9,10,12,14,15,16,18,
20,21,24,25,27。设计一个动态规划算法,求解第 n 个 Humble Number。

优化子结构及子问题重叠性

思考:动态规划问题的关键在于找到优化子结构,子结构的形式并不统一,另外个人认为同时还要考虑从子问题的重叠性来考虑,对于这题灵感就来源于子问题的重叠性。

设需要判断M是否为Humble Number,而且已经知道M之前所有的Humble Number,记为Humble_Number_Before_M,注意这是个数组。

首先,考虑的方向是如果知道Humble_Number_Before_M,那么,如何利用Humble_Number_Before_M(子问题)来判断M是否也是Humble Number,找出当前问题(数M)和子问题(Humble_Number_Before_M)的关系(优化子结构),问题就迎刃而解了。这个关系(优化子结构)并不一定是像“最长公共子序列”那样的递推关系式,但是,优化子结构的核心是:

“如果优化问题的解可以通过它的一系列子问题的解构造得到,则称该优化问题具有优化子结构”(P58)

                                                                                                                     --摘自《算法设计与分析》骆吉洲

那么数M可不可以利用它之前的Humble Number来求解(肯定不是指蛮力)呢?要知道,如果不能,那么只能是蛮力法,需要从1遍历到M/2来判断M中是否包含其他非2,3,5,7的因子,则当前的时间复杂度为O(M/2)。

 之前提到灵感来源于:考虑子问题的重叠性来源于哪里。我们知道,如果M(M>=7)是一个Humble Number,那么它的所有因子一定都来源于Humble_Number_Before_M(反证:如果M是Humble Number,且它的某个因子不在Humber_Number_Before_M中,那么与M是Humble Number 矛盾(Humble Number的定义))。例如,675是Humble Number,4725=625*7也是Humble Number,我们之前可能通过某种手段求出675是Humble Number,现在要判断4725是不是Humble Number还需要一步步先判断675是不是Humble Number吗?

分析到这,我们再多花点时间,一定能发现问题的关键之处。我们发现如果M满足:

M%2=0  AND  M/2  IN  Humble_Number_Before_M

或者

M%3=0  AND  M/3  IN  Humble_Number_Before_M

或者

M%5=0  AND  M/5  IN  Humble_Number_Before_M

或者

M%7=0  AND  M/7  IN  Humble_Number_Before_M

那么,M一定也是Humble Number(答案都给了,自己想),这样我们就充分利用了当前问题的子结构构造当前问题的解。

对于Humble_Numble_Before_M,我们可以使用关联容器Set(C++)进行存储,可以实现高效查找。下面是伪代码。

define Set<int> Hash_Humble_Number(简写为HHN)      		//key值保存已经求出的所有Humble Number
HHN={1,2,3,4,5,6,7}						//初始化前7个Humble Number   
int Humble_Numer_N;						//记录第n个Humble Number
index=8 							//记录第index个Humble Number
i=8		                                             	//开始从i向后遍历
while(index<=n) Do
    If 	(	(i%2=0 && HHN.find(i/2) = HHN.end())            //未找到key为i的元素,C++ set写法
                || (i%3=0 && HHN.find(i/3) = HNN.end())
                || (i%5=0 && HHN.find(i/5) = HNN.end())
                || (i%7=0 && HHN.find(i/7) = HNN.end())
        )  Then
        HHN.insert(i);                                  //插入第index个Humble Number i
        Humble_Numer_N=i; ++index;                      //目前已经求到第index个
    ++i                                       	        //依次遍历,直到index=n,即找到第n个Humble Number
Return Humble_Numer_N                                        
时间复杂度

从伪代码中可以看出,循环结束的条件是找到第n个Humble Number M,那么时间复杂度为:

T(n)=O(M)

总结

如果一开始就看代码,可能看不出来这是动态规划,但是如果从头去想去做就能很清楚的知道。这大概就是看人家的代码和自己写代码的区别吧。把握问题的关键点,每一个程序都可能是动态规划。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值