第 n 个丑数
自己想了两天都没思路啊啊啊啊啊我真是太笨了。看了他的 tag,说是用到了动态规划,于是特意看了算法导论里动态规划的部分。然而只是说了其思想,第一步构建合理的数据结构,第二部以递归的形式求解。可见水无常形,动态规划并不是单纯的公式就可以解决的。那么具体如何处理呢?
第一是,什么时候会用到动态规划,就是这样一种情况:后面的结果来自于前面的结果,而前面的结果又已经被求解后存储。前者意味着它的递归结构,后者有种空间换时间的意味。
明确这个就进入第二个问题,丑数是什么?
除了 1 之外,由且只由 2 3 5 三种素因子构成的数即为丑数。既然素因子只能有 2 3 5,那么后面的丑数必然是通过前面的丑数乘以 2/3/5 得到的。
后面的数由前面的求出,而前面的丑数被求出后又储存在数组中,这两点正是之前提到的递归和空间换时间。
那么新的丑数可能是怎么构成的呢?三种:2 *a/ 3 * b/ 5 * c 因此设置三个标记,分别指示其中 a, b, c 的位置。
其中,最小的数字成为新的丑数。而在其被选为新丑数之后,下一个新丑数必然要比这个数大(废话) 所以要将相应的标记后移,以确保下一个新丑数的三个选择均大于当前最大的丑数。
有了以上说明之后,就可以很好理解一下算法了:
int nthUglyNumber(int n) {
if (n < 1){
return 0;
}
vector<int> ugliness{1};
auto index2 = 0;
auto index3 = 0;
auto index5 = 0;
auto saveNewUglyIncreaseIt = [&]
{
auto const val2 = ugliness[index2] * 2;
auto const val3 = ugliness[index3] * 3;
auto const val5 = ugliness[index5] * 5;
auto const newUgly = min(val2, min(val3, val5));
if (newUgly == val2){
++index2;
}
if (newUgly == val3){
++index3;
}
if (newUgly == val5){
++index5;
}
ugliness.push_back(newUgly);
};
for (int i = 1; i != n; ++i){
saveNewUglyIncreaseIt();
}
return ugliness.back();
}