首页 > 代码库 > HDU4427Math Magic (dp+滚动数组)
HDU4427Math Magic (dp+滚动数组)
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4427
题意:
求选定k个数,k个数的和为n,最小公倍数是m的方案数,最后的结果mod 1000000007;
分析:
状态转移很好找,难的是自己去实现优化。
状态转移方程为 : dp[i+1][s+k][lcm(l,k)]+=dp[i][s][l];
第一维代表的是有多少个数,第二维代表的是这些数的和,第三维代表的是这些数的最小公倍数
因为开不了那么大的数组,因此我们要用滚动数组,详细请见注释。
代码如下:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int mod = 1000000007; const int maxn = 1005; inline int gcd(int a,int b) { if(b) return gcd(b,a%b); return a; } int dp[2][maxn][maxn]; int fac[1000]; int lcm[maxn][maxn]; void init()//预处理1~1000内的任意两个数的最小公倍数 { for(int i=1;i<maxn;i++) for(int j=i;j<maxn;j++) lcm[i][j]=lcm[j][i]=i*j/gcd(i,j); } int main() { init(); int n,m,k; while(~scanf("%d%d%d",&n,&m,&k)){ memset(dp,0,sizeof(dp)); int tmp = m, cnt=0,v=0; memset(dp,0,sizeof dp); for(int i = 1; i<=m; i++)//预处理出m的所有约数,这k个数一定是在m的约数里面选的 if(m%i==0) fac[cnt++]=i; for(int i=0; i<cnt; i++)//初始化 dp[v][fac[i]][fac[i]]=1; for(int i=1; i<k; i++) //枚举长度 { memset(dp[v^1],0,sizeof(dp[v^1])); for(int j=i; j<n; j++) //枚举sum { for(int p=0; p<cnt; p++) //枚举上一个状态的公倍数 { int mul=fac[p]; if(!dp[v][j][mul]) continue; for(int q=0; q<cnt; q++) //枚举因子 { int tt=j+fac[q];//计算当前状态的和 if(tt>n) break; int t = lcm[mul][fac[q]];//当前状态的最小公倍数 dp[v^1][tt][t]+=dp[v][j][mul];//当前状态等于之前所有可以达到这个状态的状态的和 dp[v^1][tt][t]%=mod; } } } v^=1; } printf("%d\n",dp[v][n][m]); } return 0; }
HDU4427Math Magic (dp+滚动数组)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。