【LeetCode】313.超级丑数

225 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

题目

超级丑数 是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。

给你一个整数 n 和一个整数数组 primes ,返回第 n 个 超级丑数 。

题目数据保证第 n 个 超级丑数 在 32-bit 带符号整数范围内。

示例 1

输入:n = 12, primes = [2,7,13,19]
输出:32 
解释:给定长度为 4 的质数数组 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。

示例 2

输入:n = 1, primes = [2,3,5]
输出:1
解释:1 不含质因数,因此它的所有质因数都在质数数组 primes = [2,3,5] 中。

提示

  • 1 <= n <= 105
  • 1 <= primes.length <= 100
  • 2 <= primes[i] <= 1000
  • 题目数据 保证 primes[i] 是一个质数
  • primes 中的所有值都 互不相同 ,且按 递增顺序 排列

思路

考虑新丑数的诞生方式,简单推算可得:

旧丑数序列 * 质数序列 = 新丑数序列

因为存在新旧的转移关系,可以考虑用动态规划解决此题。

因为题目要求第n个丑数,即我们需要从第一个丑数不断推移至第n个丑数,我们需要考虑已知第一个丑数,如何找出第二个丑数。

因为已知: 旧丑数序列 * 质数序列 = 新丑数序列

则我们把质数序列依次乘于第一个丑数后,就可以得到新丑数序列,新丑数序列中最小的丑数,便是第二个丑数

以此类推:已知前 n 个丑数,如何得出第 n + 1 个丑数

仍然是: 旧丑数序列 * 质数序列 = 新丑数序列

但是: 旧丑数序列依次乘于质数序列将会出现 n * n的复杂度,有没有办法优化呢?

优化方案:因为我们要求出的是下一个最小的丑数,当旧丑数序列中的 一个丑数 和 一个质数 已经贡献出过一个最小值,则它们肯定无法再次贡献出最小值,即不考虑该质数与这个丑数的乘积,只考虑该质数下的下一个丑数与它的乘积。那么,我们再定义一个pointer数组,用于记录每个质数应该与哪个旧丑数做乘积即可。

代码

class Solution {
public:
    int nthSuperUglyNumber(int n, vector<int>& primes) {
        vector<int> dp(n + 1);  //用来存储丑数序列
        dp[1] = 1;          //第一个丑数是1
        int m = primes.size();
        vector<int> nums(m);     //记录新丑数序列
        vector<int> pointers(m, 1); //记录质数该与哪一位丑数做乘积
        for (int i = 2; i <= n; i++) {
            int minn = INT_MAX;
            for (int j = 0; j < m; j++) {
                nums[j] = dp[pointers[j]] * primes[j]; //旧丑数 * 质数序列 = 新丑数序列
                minn = min(minn, nums[j]);  //寻找所有新丑数中最小的丑数
            }
            dp[i] = minn;
            for (int j = 0; j < m; j++)
                if (minn == nums[j])        //如果此位置已经诞生过最小丑数
                    pointers[j]++;          //让此位置所取旧丑数向后推一位
        }
        return dp[n];
    }
};

结语

业精于勤,荒于嬉;行成于思,毁于随。