**
描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第 n个丑数。
数据范围:
0≤n≤2000
要求:空间复杂度 O(n) , 时间复杂度 O(n)
示例1
输入:7
返回值:8
题目分析
我们先看到题目,把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。
有了上面的定义我们就可以知道,丑数的形式就是2x3y5z.所有的丑数都会是由2x3y5^z组成的.
因此我们只需要根据此公式从小到大列举出每一个丑数,那么我们就能找到第n个丑数.
由于,x,y,z都是幂次方,且如果要从小到大寻找丑数,那么x,y,z都会逐渐增加,若想找到后一个幂次方的丑数,那么我们肯定会需要找到前一个幂次方的丑数.这时候我们就会想到用动态规划来解决此问题,因为我们可以将第n个丑数一直分解到第一个丑数,根据动态规划的转移方程,我们就可以一步步找到第n个丑数.
题解
说到动态规划,我们肯定会想到动态规划的三个步骤
1.确定状态
2.定义状态转移方程
3.求得最优解
1.确定状态
首先我们应该确定动态规划的起始状态,根据题干,我们能发现第一个丑数就是,我们即dp[0]=1,且此时x,y,z的值肯定都是0,即x=0,y=0,z=0.
2.定义状态转移方程
状态转移方程是这个题目的难点,我们需要知道如何通过状态转移方程来实现下一个丑数的定位,我们通过分析函数dp[i]=2x3y5z(其中i是第i个丑数的下标存储位置)可知,我们只要分别增大幂x,y,z,(x,y,z都从0开始增大)然后进行比较,我们就能找到我们想要的下一个丑数。
比如dp[0]=1,那么dp[1]的值,就是需要比较
213050 ,203150 ,203051 这三个值的大小,得出dp[1]=213050 = 2
因为213050 已经给dp[1],因此需要将第一个位置增大继续进行比较,那么如何进行增大呢?
这个地方也是本题最难理解的地方。我们先回到本题dp[i]=2x3y5z 这个函数上,我们可以想一下dp[i+1]有什么规律,dp[i+1]无非就是dp[i-k]*2,dp[i-k]*3,dp[i-k]*5 (k<i),三个其中的一个值,其中K可以是任意一个值,不一定是1.我们也可以反过来想,dp[i+1]/2,dp[i+1]/3,dp[i+1]/5的其中一个值,一定是dp[i]。所以我们这里只需要列出三路函数,第一路是关于dp[x]*2的函数,第二路是关于dp[y]*3,第三路是关于dp[z]*5的函数然后一直比较,就可以逐一找到每个丑数。其中x,y,z都是每一路线性增长的下标。这里需要解释一下为什么要分三路,因为d[i]的每次结果都需要乘2,乘3和乘5,以达到所有的质数都会被比较到的目的。且dp[i]乘2,乘3,乘5的值比较的时机是不同的。所以必须分为3个下标来分别统计幂x,y,z增长的值,从而进行动态规划比较。
求dp[2]:
根据上面的规则,第一个位置增大的值是dp[x=1]*2=(213050)*2=4
所以dp[2]的值,需要比较
223050 ,203150 ,203051 这三个值的大小,得出dp[2]=203150 = 3
求dp[3]:
同理第二个位置被取出,增大第二个位置,即dp[y=1]*3=(213050)*3=6
所以dp[3]的值,需要比较
223050 ,21315^0 ,203051 这三个值的大小,得出dp[3]=223050 = 4
求dp[4]:
后面为了便于观看,将直接用数字来代替,
接着第一个位置被取出,所以x需要继续增加,即dp[x=2]*2=3*2=6
dp[4]的值,需要比较
6 ,6 ,5 这三个值的大小,得出dp[4]=5
求dp[5]:
第三个数增大为dp[z=1]*5=2*5=10
dp[5]的值,需要比较
6 ,6 ,10 这三个值的大小,得出dp[5]=6,这时,一二位置都为6,所以都要增大,
即第一个位置dp[x=3]*2=4*2=8,第二个位置dp[y=2]*3=9
所以dp[5]的值需要比较
8,9,10这个三个数,得出dp[5]=8.
3.求得最优解
以此类推,就能列举出所有的丑数了。
下面是一个更为直观的动画图来解释此过程:
接下来就是算法部分:
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param index int整型
* @return int整型
*/
public int GetUglyNumber_Solution (int index) {
if(index<=6){
return index;
}
int[] dp = new int[index];
dp[0] = 1;
int x=0,y=0,z=0;
for(int i=1;i<dp.length;i++){
dp[i] = Math.min(dp[z]*5,Math.min(dp[x]*2,dp[y]*3));
if(dp[i] == dp[x]*2){
x++;
}
if(dp[i] == dp[y]*3){
y++;
}
if(dp[i] == dp[z]*5){
z++;
}
}
return dp[index-1];
}
}