题意简述
定义因子只有2或3或5的数为丑数,求第n个丑数。
算法分析
法一 最小堆
每次取出堆顶x,插入2x,3x,5x。set
或priority_queue
。复杂度nlogn。
法二 动态规划
每个丑数,乘以2乘以3乘以5都能得到3个更大的丑数。反过来看每个丑数,都是由之前的某个丑数乘以2乘以3乘以5得到。为了得到f[i]是由哪一个推得,考虑维护p2p3p5三个指针,分别指向当前乘以2乘以3乘以5之后可能更新最大丑数(即f[i−1])的之前的某个丑数下标。
定义f[i]为第i个丑数,指针px(x=2,3,5)为
minjs.t. f[j]×x>f[i−1]
每次求第i个丑数时,取
min\left\\{
f[p_x] \times x
\right\\}
之后比较f[i]与f[px]×x(x=2,3,5),将对应的指针加一。
举例来看,第一个丑数1,有资格乘以2乘以3乘以5,取出2作为第二个丑数。之后,由于1已经乘以过2,1只有乘以3乘以5的资格(可能性)了,对应的操作就是p2加一。
从另一个角度看,为什么这样选择px就一定是能更新最大丑数的最小下标呢?这是因为只有当f[i]=f[px]×x时才会移动px。也就是说,对于任意一个小于px的下标p,f[p]×x一定被考虑过了(已经出现在f里面了),自然也就无法超过f[i−1]。
代码实现
| 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]; } };
|