leetcode 264 丑数 II

题意简述

定义因子只有223355的数为丑数,求第nn个丑数。

算法分析

法一 最小堆

每次取出堆顶xx,插入2x,3x,5x2x,3x,5xsetpriority_queue。复杂度nlognnlogn

法二 动态规划

每个丑数,乘以22乘以33乘以55都能得到33个更大的丑数。反过来看每个丑数,都是由之前的某个丑数乘以22乘以33乘以55得到。为了得到f[i]f[i]是由哪一个推得,考虑维护p2p3p5p_2p_3p_5三个指针,分别指向当前乘以22乘以33乘以55之后可能更新最大丑数(即f[i1]f[i-1])的之前的某个丑数下标。

定义f[i]f[i]为第ii个丑数,指针px(x=2,3,5)p_x(x=2,3,5)

minjs.t. f[j]×x>f[i1] min\\{j\\} \quad s.t. \ f[j] \times x > f[i - 1]

每次求第ii个丑数时,取

min\left\\{ f[p_x] \times x \right\\}

之后比较f[i]f[i]f[px]×x(x=2,3,5)f[p_x] \times x(x=2,3,5),将对应的指针加一。

举例来看,第一个丑数11,有资格乘以22乘以33乘以55,取出22作为第二个丑数。之后,由于11已经乘以过2211只有乘以33乘以55的资格(可能性)了,对应的操作就是p2p_2加一。

从另一个角度看,为什么这样选择pxp_x就一定是能更新最大丑数的最小下标呢?这是因为只有当f[i]=f[px]×xf[i]=f[p_x] \times x时才会移动pxp_x。也就是说,对于任意一个小于pxp_x的下标ppf[p]×xf[p] \times x一定被考虑过了(已经出现在ff里面了),自然也就无法超过f[i1]f[i - 1]

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int nthUglyNumber(int n) {
vector<int> f(1700, 0);
int p2 = 1, p3 = 1, p5 = 1;
f[1] = 1;

for (int i = 2; i <= n; i++) {
f[i] = min({f[p2] * 2, f[p3] * 3, f[p5] * 5});
if (f[i] == f[p2] * 2) p2++;
if (f[i] == f[p3] * 3) p3++;
if (f[i] == f[p5] * 5) p5++;
}
return f[n];
}
};