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;
}