hdu 4345 Permutation

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;

}

  

你可能感兴趣的:(HDU)