题目
思路
本题要求我们求出第n个丑数,对于某个丑数,我们最直观的感觉就是该丑数是由前面的某个丑数x2或者x3或者x5而得,所以本题的难点就在于:如何通过以前所求的丑数求某个位置的丑数
让我们首先按照动态规划的一般思路去思考:本题似乎是存在三个维度:x2的,x3的,x5的,那么如果我们设置三个dp数组,分别表示这三个维度,那么我们该如何推进状态呢,也就是说在设置这三个维度的情况下,如何求出状态转移方程呢?而且这三个维度之间也存在重叠的元素,那么这又该如何处理呢?似乎设置三个维度是增加了求状态转移方程的难度(也可能设置三个维度求不出状态转移方程)
那我们从最简单的思路入手,我们只设置一个维度,也就是只设置一个dp数组,dp[i]表示第i个位置的丑数,那么dp[i]如何从前面所求的丑数求得呢?
我们发现,某个位置的丑数是前面某个丑数x2,x3,x5(这里要注意的是,这三个乘以的丑数可能都不同)所得的最小值,也就是三个维度之间相互竞争,最小的胜出,所以状态转移方程就为:dp[i]=min(2dp[i2],3dp[i3],5*dp[i5]),总结一下具体思路就是:
- 设置三个指针,分别指向三个维度参与竞争的丑数
- 最小的胜出,同时该维度的竞争者为下一个(即后移一位指针)
- 依照上面的规则进行竞争,直到得出第n个丑数
相关代码如下:
class Solution {
public:
int nthUglyNumber(int n) {
int i2 = 0, i3 = 0, i5 = 0;
vector<int> res(n);
res[0] = 1;
int i = 0;
while (++i < n) {
res[i] = min(min(2 * res[i2], 3 * res[i3]), 5 * res[i5]);
if (res[i] == 2 * res[i2]) i2++;
if (res[i] == 3 * res[i3]) i3++;
if (res[i] == 5 * res[i5]) i5++;
}
return res.back();
}
};
上述代码中值得注意的一点就是:在判断是哪个胜出的时候,使用的是三个if,这样可以解决元素重复问题
总结
本题相对于之前所做的动态规划类的题目来说,一个新颖点就是增加了指针运算,因为某个位置的丑数可能由前面的某个丑数乘2或者乘3或者乘5得来,如果我们设置三个dp数组的话,并不是很好的求解,我们抓住一个关键点就是某个位置的丑数是min(一个丑数x2,一个丑数x3,一个丑数x5),那么我们就设置三个指针,分别指向要参与竞争的三个维度对应的三个丑数,胜出的那个后移一位再参与下一轮的竞争