Poj 3421-X-factor Chains

来源:互联网 发布:淘宝助理收费吗 编辑:程序博客网 时间:2024/05/23 17:47

 Poj 3421-X-factor Chains

题意:给定数x,求一条最长的因子链,1 = X0, X1, X2, …, Xm = X,链中xi<xi+1,且xi整除xi+1,求最长的链的长度和其数量。


因为前段时间要进行考试的预习,每天在自习室呆着,很久没写代码了,好不容易考试完,继续我的刷题之旅,结果一写就gg了…..虽说是个简单题,但是太弱,所以卡了很久,决定纪念一下我谜一般的思路过程…..

1、 刚开始做的时候一眼看上去像个图论题,把x的所有把整除关系看成边,x的因子看成顶点,之后dfs进行图的遍历就行了,找到最长的和其长度,因子稍微多点就基本运行不出结果了,于是进行了一个最优性剪枝,感觉一个数据能很快运行出结果了,直接就交上去,然后超时了,也是无语。

#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <utility>#include <vector>#include <map>using namespace std;map<int, vector<int> > mp;vector<int> d;map<int, int> l;int ans, num;void dfs(int x, int len) {    if (len < l[x]) return ;    else l[x] = len;    if  (len >= ans) {        if (len == ans) num++;        else ans = len, num = 1;    }    for (int i = 0; i < mp[x].size(); i++) dfs(mp[x][i], len+1);}int main() {    int x;    while (~scanf("%d", &x)) {        d.clear(); mp.clear(); l.clear();        int t = sqrt(x+0.0);        d.push_back(1), d.push_back(x);        for (int i = 2; i < t; i++)            if (x % i == 0) d.push_back(i), d.push_back(x/i);        if (t*t == x) d.push_back(t);        sort(d.begin(), d.end());        for (int i = 0; i < d.size(); i++) {            for (int j = i+1; j < d.size(); j++)                if (d[j] % d[i] == 0) mp[d[i]].push_back(d[j]);        }        ans = 0, num = 1;        dfs(d[0], 0);        printf("%d %d\n", ans, num);    }    return 0;}


2、 想来想去,觉得深搜再怎么剪也肯定不行,想了一下dp,对于一个数x,计算两个值,一个是x为最末尾元素的链的最长长度,另一个是这种长度的数量,状态转移为其因子中那些链最长的,然后记忆化搜索的方式实现比较好,跑了一下数据,比之前的深搜快了几十倍,马上高兴地交了上去,还是超时,复杂度已经很低了,这都超时!无奈了,没办法进行优化了,按理来说不应该啊,题目不是说several个test case吗,而且时间限制还有2s,我跑10多个因子数很多的都只要1s左右,其实题目的数据量应该不会只有several个……

#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <utility>#include <vector>#include <map>using namespace std;typedef pair<int, int> p;map<int, vector<int> > mp;map<int, p> l;vector<int> d;p dp(int x) {    if (x == 1 || l[x].first) return l[x];    vector<p> vec;    for (int i = 0; i < mp[x].size(); i++) vec.push_back(dp(mp[x][i]));    int len = vec[0].first, num = vec[0].second;    for (int i = 1; i < vec.size(); i++) {        if (len < vec[i].first) len = vec[i].first, num = vec[i].second;        else if (len == vec[i].first) num += vec[i].second;    }    return l[x] = p(len+1, num);}int main() {    /*freopen("in.txt", "r", stdin);    freopen("out.txt", "w", stdout);*/    int x;    while (~scanf("%d", &x)) {        d.clear(); mp.clear(); l.clear();        l[1] = p(0, 1);        int t = sqrt(x+0.0);        d.push_back(1), d.push_back(x);        for (int i = 2; i < t; i++)            if (x % i == 0) d.push_back(i), d.push_back(x/i);        if (t*t == x) d.push_back(t);        sort(d.begin(), d.end());        for (int i = 0; i < d.size(); i++) {            for (int j = i+1; j < d.size(); j++)                if (d[j] % d[i] == 0) mp[d[j]].push_back(d[i]);        }        p ans = dp(x);        printf("%d %d\n", ans.first, ans.second);    }    return 0;}

3、 经历了前两次的失败,没什么想法了,只能翻开挑战程序设计实践的数论部分随便看看找点思路了,看到习题练习上那题的标签是素数,于是想到从素数下手,很快就想到了素数筛法,利用筛法的思想进行状态转移,对于一个数x,状态转移至其所有的倍数,紫书上面给出了证明,时间复杂度为O(nlogn),不过转念一想,这样应该比刚刚的复杂度还高啊,写完后交上去果然又超时了…..打算做最后的反抗,看还能不能在优化下,其实在求解答案的过程中,比x小的数的所有数的最长链的长度和数量全部计算出来了,根本不需要每次都O(nlogn)计算一下答案,直接预处理一下所有的数,复杂度也只有O(2^20*20),肯定不会超时,交上去终于过了!!!不过跑了900多ms。

#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int N = 1048576;typedef pair<int, int> p;p a[N];void predeal() {    for (int i = 1; i <= 524288; i++) {        for (int j = 2*i; j <= N; j += i) {            if (a[i].first + 1 > a[j].first) a[j] = p(a[i].first + 1, a[i].second);            else if (a[i].first + 1 == a[j].first) a[j].second += a[i].second;        }    }}int main() {    int x;    a[1] = p(0, 1);    predeal();    while (~scanf("%d", &x))printf("%d %d\n", a[x].first, a[x].second);    return 0;}

4、 最后重新思索一下这个题目,写了一个数模拟下计算的过程,发现了更巧也更简单的思路,其实要使得链尽量长,那么链中的元素前后两个数中大的比小的多一个素因子相乘即可,这样链肯定是最长的,那么数量怎么求呢?其实就是选取素因子相乘的排列问题,这样便转化为了多重集排列数的计算,直接套用离散数学书上的公式即可:

设多重集S={n1*a1,n2*a2,….nk*ak},且n=n1+n2+n3+…+nk,则s的排列数为n!/(n1!*n2!*…nk!)

所以对于一个数x,对其进行唯一分解式的处理,找出所有的素因子和他们的数量。交上去110ms….


#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <map>using namespace std;typedef long long LL;map<int, int> d;void prime_factor(int x) {    for (int i = 2; i * i <= x; i++) {        while (x % i == 0) x /= i, d[i]++;    }    if (x != 1) d[x]++;}LL fact(int x) { return x == 1 ? 1 : x * fact(x-1); }int main() {    int x;    while (~scanf("%d", &x)) {        if (x == 1) { printf("0 1\n"); continue; }        d.clear(); prime_factor(x);        int len = 0;        for (map<int, int>::iterator i = d.begin(); i != d.end(); i++) len += i->second;        LL num = fact(len);        for (map<int, int>::iterator i = d.begin(); i != d.end(); i++) num /= fact(i->second);        printf("%d %I64d\n", len, num);    }    return 0;}

最后重新思索整个过程,还真是感受颇多,一片乱搞,卡了很久才过,从这个简单的数论题中发现了自己数学思维是有多差,不过很多题目都是这样,解题的关键点可以在计算过程中发现或者猜测出来,但是更重要的还是多刷题锻炼这些东西…


0 0
原创粉丝点击