第一感觉是用类似于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; }