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