上道Score Inflation已经是去年10月的事情了,之前卡在了Humble Numbers这道题了,其实这道在很早之前就做过了,在POJ上,不过给忘了,也不知道怎么做了。先说下题意,给定K个素数,求出第N个丑数,丑数的定义是,若干个数的乘积,而这些数都来自于之前给定的K个素数的集合。
想了个很暴力的方法,用一个最小堆来维护当前的最小的丑数,然后POP出去,用这个数和那K个素数相乘,判定是否唯一,然后放进最小堆,早就知道不行,还很弱的去写了一下,用STL实现,更加超时了,复杂度是O(K*N)。
参考了这,这,官方的做法是用一个表,有序放丑数,假设有m个,第m+1个丑数,用素数集合中的数去和m个丑数相乘,第一个和某数相乘的丑数大于第m个丑数的话,它就有可能是第m+1个丑数,这些可能的值,取个最小的就是第m+1个丑数了,这样还不行,复杂度其实还是O(K*N),可以用二分的方法来加速,但也没把复杂度降下来。于是这个优化就很强大了,用一个数组记录乘积大于第m个丑数的位置,下次不从头开始枚举,而是从该位置开始枚举,就可以把复杂度降到O(K+N)了。
相比这些,我的那个暴力的方法每次不仅仅把第N个的算出来了,还把很多没用的数也算了出来,代码如下:
/*
ID: litstrong
PROG: humble
LANG: C++
*/
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <math.h>
#include <queue>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
const int MAX = 105;
const int MAXN = 100005;
int num[MAX], m_index[MAX];
int prime[MAXN];
int K, N;
class CNUM
{
public:
int m_val;
CNUM() {}
CNUM(int m) : m_val(m) {}
bool operator < (const CNUM &t) const
{
return m_val > t.m_val;
}
};
int go()
{
priority_queue<CNUM> Q;
set<int> one;
sort(num, num + K);
Q.push(1);
for(int i = 0; i <= N; i++)
{
int m_min = Q.top().m_val;
//printf("%d %d\n", i, m_min);
if(i == N) return m_min;
Q.pop();
for(int j = 0; j < K; j++)
{
int m_num = m_min * num[j];
if(one.find(m_num) == one.end())
{
one.insert(m_num);
Q.push(CNUM(m_num));
}
}
}
return 0;
}
int go_ans()
{
sort(num, num + K);
memset(m_index, 0, sizeof(m_index));
int top_num = 0;
prime[top_num++] = 1;
while(top_num <= N)
{
int m_min = -1;
for(int i = 0; i < K; i++)
{
while(num[i] * prime[m_index[i]] <= prime[top_num - 1] && m_index[i] < top_num)
m_index[i]++;
if(m_index[i] < top_num)
{
if(m_min == -1 || num[i] * prime[m_index[i]] < m_min)
m_min = num[i] * prime[m_index[i]];
}
}
prime[top_num++] = m_min;
}
return prime[N];
}
int main()
{
//freopen("humble.in", "r", stdin);
//freopen("humble.out", "w", stdout);
int k, n;
scanf("%d%d", &K, &N);
for(int i = 0; i < K; i++)
scanf("%d", &num[i]);
printf("%d\n", go_ans());
}