http://acm.hdu.edu.cn/showproblem.php?pid=4345

记忆化搜索 dp  比赛的时候没想出来呀亲

此题和 置换群有那么丁点关系 但关系不大

题目让我们求的是 实际是相加合为n的若干整数 他们的最小公倍数有多少种

由于1不会影响最小公倍数所以小于等于n的都可以

我们可以这么想在这个整数集合里 我们不让一个质数和它的幂次数同时出现

这样的话集合里面的所有数都互质 这样就求种类数就容易递推了

假如说 n=6  小于它的最小质数是2  对于其它可能存在的质数(只是可能)的质数 (3 ,  5)

首先 2 可以不存在  是一种情况

然后2存在  2和其它的质数互质 所以2和集合可以组成的任意最小公倍数相乘都得到一个新的最小公倍数  除了 2 ,还有4  8  16等 2的幂次放  比如说6 是不可以的因为6和3不互质

把它的幂在一定范围内枚举就可以了

最后要把所以种类相加就是答案

代码及其注释:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#define LL long long

using namespace std;

const int N=1005;
const int M=1100;
LL ans[N][N];
int a[1100];
int m;
void begin()
{
    bool k[M];
    memset(k,true,sizeof(k));
    int I=0;
    for(int i=2;i<M;++i)
    {
        if(k[i])
        {
            a[I++]=i;//用来记录第几个质数是多大  注意求得的最后一个质数要比N大
            for(int j=i*2;j<M;j=j+i)
            k[j]=false;
        }
    }
}
LL dp(int n,int i)//对于合小于等于n的情况 质数从第i个开始种类
{
    if(ans[n][i]!=-1)//你懂得
    return ans[n][i];
    if(i>m)//边界   此时n若不为0 我们可以认为剩下的全是1
    {
        ans[n][i]=1;
        return ans[n][i];
    }
    ans[n][i]=dp(n,i+1);//首先是第i个质数不存在的情况
    int k=a[i];
    while(k<=n)
    {
        ans[n][i]+=dp(n-k,i+1);//然后枚举他的幂次数存在
        k=k*a[i];
    }
    return ans[n][i];
}
int main()
{
    //freopen("data.txt","r",stdin);
    int n;
    begin();
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;++i)
        {
            if(a[i]>n)
            {
                m=i-1;//n内可以到达的最大质数是第m个(下标从0开始)
                break;
            }
        }
        memset(ans,-1,sizeof(ans));
        cout<<dp(n,0)<<endl;
    }
    return 0;
}

  

posted on 2012-08-08 10:33  夜->  阅读(585)  评论(0编辑  收藏  举报