第一感觉是用类似于treap的方法,不过treap我没有独立实现过,怕细节上出错,但是思想还是可以借鉴的,于是就想到用STL的set来帮忙。看下面的图就知道了。只要在树中的节点就是可以扩展的节点,所以绿色嘛,黑色表示正在扩展的节点,蓝色就是扩展出来的数字。节点中的两笔数据 value 和 next,value就是当前节点已经扩展到了哪个humble数字,next就是接下来要扩展时,value需要乘以第几个输入的质数。
不过,有点慢,虽然也不是很慢。复杂度的话,最坏时树中节点数kn,所以应该就是O(nlogkn)。确实不算很快,所以我又写了后来的动态规划的版本,飞起来了。不过写贴上这个版本的代码吧。
/*
ID:fairyroad
LANG:C++
TASK:humble
*/
#include<fstream>
#include<set>
using namespace std;
ifstream fin("humble.in");
ofstream fout("humble.out");
size_t k, n;
long long p[100];
struct node
{
long long value;
size_t next;
node() : value(0), next(0) {}
node(long long v, size_t n) : value(v), next(n) {}
};
struct comp
{
bool operator()(const node& p1, const node& p2)
{
return p1.value*p[p1.next] < p2.value*p[p2.next];
}
};
int main()
{
fin>>k>>n;
for(size_t i = 0; i < k; ++i)
fin>>p[i];
set<node, comp> coll;
set<node, comp>::iterator pos;
coll.insert(node(1, 0));
size_t num = 0, idx;
long long min, v;
while(num < n)
{
++num;
pos = coll.begin();
v = pos->value, idx = pos->next;
min = v*p[idx];
if(idx+1 < k)
coll.insert(node(v, idx+1));
coll.erase(pos);
coll.insert(node(min, idx));
}
fout<<min<<endl;
return 0;
}
值得一提的是,空间复杂度也是很高的。看测试结果就知道了,算是不优雅的通过了。
动态规划的基本思路是,我们在数组hum中计算出前n个丑数。当我们已知前k个丑数,想得到第k+1个时,就去寻找对于每个质数p的寻找最小的丑数h,使得h*p比第k个丑数大。复杂度是O(KN),空间复杂度更是稳定的O(N),实在是精彩啊!
好像也不是多么严格的动态规划,但是符合记忆化搜索的思维。思维最重要,具体是不是倒是其次了。
/*
ID:fairyroad
LANG:C++
TASK:humble
*/
#include <fstream>
#include<climits>
using namespace std;
ifstream fin("humble.in");
ofstream fout("humble.out");
int k, n;
int p[100], start[100];
long long res[100001];
int main()
{
fin>>k>>n;
for(int i = 0; i < k; ++i)
fin>>p[i], start[i] = 0;
int num = 0;
res[num++] = 1;
while(num <= n)
{
int index = 0;
long long min = LONG_LONG_MAX;
for(int i = 0; i < k; ++i)
{
while(p[i] * res[start[i]] <= res[num-1])
++start[i];
if(p[i] * res[start[i]] < min) {
min = p[i] * res[start[i]];
index = i;
}
}
res[num++] = min;
++start[index];
}
fout<<res[n]<<endl;
return 0;
}