hdu 4427 DP

题意:

给你k个数的和和k个数的最小公倍数。问你一共有多少满足条件的解。

用三维来表示状态。

dp[i][j][k]。表示长度为i。和为j。最小公倍数为k的方法数。

设a为解的第i+1个数。

那么状态转移就为

dp[i+1][j+a][lcm(a,k)]+=dp[i][j][k]。

lcm为最大公倍数。

由于三维开不下就只能用滚动数组了。

为了节约时间先预处理出1000以内任意两数的最小公倍数。注意一点。一个数学常识。如果这k个数的最小公倍数是

x。那么这k个数肯定都是x的因子。所以先预处理出x的因子。这样就可以有效的减少枚举范围。还有memset要慎用尤其是在这种数量级下。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cmath>
 5 #include <memory.h>
 6 using namespace std;
 7 typedef long long ll;
 8 const int mod = 1e9 + 7;
 9 const ll maxn = 1e5 + 10;
10 int dp[2][1005][1005],fac[1005],cnt,lcm[1005][1005];
11 int gcd(int a,int b){
12     return b == 0?a:gcd(b,a%b);
13 }
14 void init(){
15     for(int i = 1;i <= 1000;i++)
16         for(int j = i;j <= 1000;j++)
17             lcm[i][j] = lcm[j][i] = i*j/gcd(i,j);
18 }
19 int n,m,k;
20 int main() {
21     //freopen("in.txt","r",stdin);
22     init();
23     while(~scanf("%d%d%d",&n,&m,&k)){
24         memset(dp,0,sizeof(dp));
25         memset(fac,0,sizeof(fac));
26         cnt = 0;
27         for(int i = 1;i <= m;i++)
28             if(m % i == 0) fac[++cnt] = i;
29         int id = 0;
30         for(int i = 1;i <= cnt;i++)
31             dp[id][fac[i]][fac[i]] = 1;
32         for(int i = 1;i <= k - 1;i++){
33             for(int t1 = 1;t1 <= n;t1++)
34                 for(int t2 = 1;t2 <= cnt;t2++)
35                     dp[id^1][t1][fac[t2]] = 0;
36             for(int j = 1;j <= n;j++){
37                 for(int t = 1;t <= cnt;t++){
38                     if(!dp[id][j][fac[t]]) continue;
39                     for(int x = 1;x <= cnt;x++){
40                         if(j + fac[x] > n) break;
41                         dp[id^1][j+fac[x]][lcm[fac[t]][fac[x]]] += dp[id][j][fac[t]];
42                         dp[id^1][j+fac[x]][lcm[fac[t]][fac[x]]] %= mod;
43                     }
44 
45                 }
46 
47         }
48             id ^= 1;
49         }
50     printf("%d\n",dp[id][n][m]);
51     }
52 
53 
54     return 0;
55 }

 

posted @ 2016-10-14 19:23  十目  阅读(150)  评论(0编辑  收藏  举报