题目
超级丑数 是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。
给你一个整数 n 和一个整数数组 primes ,返回第 n 个 超级丑数 。
题目数据保证第 n 个 超级丑数 在 32-bit 带符号整数范围内。
提示:
1 <= n <= 106
1 <= primes.length <= 100
2 <= primes[i] <= 1000
题目数据 保证 primes[i] 是一个质数
primes 中的所有值都 互不相同 ,且按 递增顺序 排列
示例 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] 。
思路
采用最小堆求解超时,官方题解采用动态规划进行解决。
因为并不是dp常用的最小值问题,较难想到,因此记录一下。
首先我们来思考一下最小堆的求解方法:
- 把1压入堆,作为第一个丑数
- 将堆中最小元素弹出,并将这个元素乘primes[j]后的所有元素入堆。
- 直到弹出n个元素(丑数),结束。
可以发现,其中很多元素是用不到的。比如primes=[2,……,100], 如果n=10,根本不需要100,也不需要每次弹出的值t运算后的t*100。这是不太需要的,每次堆插入一个元素,时间复杂度为O(log n)。
那怎么能解决这一个问题呐?
首先抛除堆这种数据结构的思维,防止思维定势。
既然问题就出:在于弹出的第i个元素t,到底需要不需要计算t*primes[j]。
如果第i-1个元素ti-1*primes[j]就很大,那么t就不需要乘primes[j]了,因为第i-1个丑数一定小于第i个丑数。说明我们可以先记录最小的两者相乘的值minn,万一这个值被作为第i个丑数(i<n),再计算下一个。
这么来看,我们就需要定义三个数组:
- dp:用来记录第i个丑数是谁
- nums:记录还没被标为第i个丑数的最小的t*primes[j]
- points:记录当前nums[j]是第i个丑数dp[i]与points[j]相乘。
我们发现dp[i]的值受到了前面的dp[i-x]的影响,可以当作动态规划来考虑。
但是因为此处动态规划和平时使用流程不同,状态转移方程难以理解,容易产生误导,就不进行表示了。
具体流程如下:
- 从下标1-n依次遍历dp数组,并更新dp[i]
- 对于每一次循环:
- 找出nums中的最小值nums[j]=minn(找出还没进dp的最小丑数)
- 更新dp[i]为minn
- nums中为minn的数nums[j]因被确定为第i个丑数了,因此需要对nums[j]进行更新:
- 首先points[j]+=1,意思是primes[j]需要和dp中下一个数相乘了(如果minn是dp[i]*primes[j]的结果,那么minn使用后,就需要更新这个i,使nums[j]=dp[i]*primes[j],而此时points[j]记录的则是这个i)
- nums[j]=dp[points[j]+=1]*primes[j]
- 输出dp[n]
代码
class Solution {
public:
int nthSuperUglyNumber(int n, vector<int>& primes) {
vector<int> dp(n+1);
int len = primes.size();
//标记primes[i]下一个要乘的位置
vector<int> points(len);
//标记当前primes[i]乘完后的结果
vector<int> nums(len, 1);
for(int i=1;i<=n;i++){
int minn = INT_MAX;
//找出nums最小值(找出还没进dp的最小丑数)
for(int j=0;j<len;j++){
minn = min(nums[j], minn);
}
//更新dp[i]
dp[i] = minn;
//更新points和nums的值
for(int j=0;j<len;j++){
if(nums[j]==minn){
points[j]++;
//进行乘法溢出判断
nums[j] = INT_MAX/primes[j]>=dp[points[j]]? dp[points[j]]*primes[j]:INT_MAX;
}
}
}
return dp[n];
}
};
//优先队列超时
// class Solution {
// public:
// int nthSuperUglyNumber(int n, vector<int>& primes) {
// priority_queue<long long, deque<long long>, greater<long long>> f;
// unordered_set<long long> uset;
// f.push(1);
// while(n){
// long long t = f.top();
// f.pop();
// if(uset.count(t)) continue;
// uset.emplace(t);
// n--;
// if(n==0) return t;
// for(int i=0;i<primes.size();i++){
// f.push(t*(long long)primes[i]);
// }
// }
// return 0;
// }
// };