Write a program to find the nth super ugly number.
Super ugly numbers are positive numbers whose all prime factors are in the given prime list
primes of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12
super ugly numbers given primes = [2, 7, 13, 19] of size 4.Note: (1) 1 is a super ugly number for any given primes.
(2) The given numbers in primes are in ascending order.
(3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000.
这一题主要是理清楚数学上怎么回事,factor是因子,prime factor就是质数因子。给定几个质数,产生一个序列,序列里的数包含的质数因子必须在这几个数当中。
设一个数X, 假设它可以分解为X = a * b, 如果a, b都是质数,则a, b必须都在prime里,如果b是合数,那分解b如果得到质数也必须在prime里,所以b也属于最后要求的数一种,所以以此类推,后面产生的数一定可以通过给定的prime序列和已有的前面的结果相乘得到。
很容易想到的一个问题就是如何不重复不遗漏,下面介绍本题基本方法.
方法一:复杂度O(n*k)
C++源码:
int nthSuperUglyNumber(int n, vector<int>& primes) {
vector<int> res(n); // 记录输出结果
int k = primes.size();
vector<int> pos(k, 0); // 记录每一个prime质数已经乘过的位置
int i, j, temp;
res[0] = 1; // 1是固定的起始位置
for (i = 1; i < n; i++) {
temp = INT_MAX;
for (j = 0; j < k; j++) {
temp = min(temp, res[pos[j]] * primes[j]);
}
for (j = 0; j < k; j++) {
if (temp == res[pos[j]] * primes[j])
pos[j]++;
}// 这个for循环是为了找出上一轮中所有的最小值,所有都加一,消除重复,否则下一轮最小还是它
res[i] = temp;
}
return res[n-1];
}
方法二: 复杂度O(nlogk)
C++:
struct Node {
int index, val, prime;
Node(int in, int va, int pr) {
index = in;
val = va;
prime = pr;
}
bool operator < (const Node& b) const{
return val > b.val;
} // 新定义这个大于变小于只是为了把priority queue里默认的maxheap改成minheap,也可在定义处改
};
class Solution {
public:
int nthSuperUglyNumber(int n, vector<int>& primes) {
vector<int> res(n);
priority_queue<Node> q;
res[0] = 1;
int k = primes.size();
for (int i = 0; i < k; i++) {
q.push(Node(0, primes[i], primes[i]));
} // 把所有的prime归置到这个queue里,0代表目前大家的index,val等于prime基本值因为输出第一
// 个是1, prime[i]乘以1还是自己
for (int i = 1; i < n; i++) {
Node cur = q.top();
res[i] = cur.val; //直接将最小的赋值给输出现有位置
do {
cur = q.top();//再取一次是因为如果产生循环,第二次的cur需要重新取新的top
q.pop();
cur.val = cur.prime*res[++cur.index];
q.push(cur);
} while (!q.empty() && q.top().val == res[i]);
}// 先执行再判断,避免有多个相同最小值
return res[n - 1];
}
};
这题用不用数据结构区别不大,算法复杂度相似,第二种分析起来因为每次都是对优先队列操作,也就是几次logk的操作,方法一种是将k长序列线性过了两边,一边找最小,一边改所有最小。看起来是二要快一些,实际上leetcode结果一更快。。。whatever,这个无所谓。两种都把问题较好的解决了。