设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 表示的是 前 i i i个数,总和为 j j j ,LCM 为 k k k 的时候的种类数。
如果现在已知 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k],那么我们可以求得下一个状态:
设下一个数为 s s s ,那么 d p [ i + 1 ] [ j + s ] [ l c m [ k , s ] ] dp[i+1][j+s][lcm[k,s]] dp[i+1][j+s][lcm[k,s]] 状态可以由 d p [ i + 1 ] [ j + s ] [ l c m [ k , s ] ] dp[i+1][j+s][lcm[k,s]] dp[i+1][j+s][lcm[k,s]] 转移过来,故 d p [ i + 1 ] [ j + s ] [ l c m [ k , s ] ] dp[i+1][j+s][lcm[k,s]] dp[i+1][j+s][lcm[k,s]] 加上 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 的值即可。
那么此题需要空间优化以及时间剪枝优化。
由于操作的是三维数组上下两层,故可以用滚动数组优化空间复杂度。
再一点的是:
对于
d
p
[
i
+
1
]
[
j
+
s
]
[
l
c
m
[
k
,
s
]
]
dp[i+1][j+s][lcm[k,s]]
dp[i+1][j+s][lcm[k,s]] 。这个是由
d
p
[
i
+
1
]
[
j
+
s
]
[
l
c
m
[
k
,
s
]
]
dp[i+1][j+s][lcm[k,s]]
dp[i+1][j+s][lcm[k,s]] 转移过来的,那么一定要满足以下条件:
1 、 j + s < = n ( n 为 总 和 ) 1、j + s <= n (n 为总和) 1、j+s<=n(n为总和)
2 、 l c m [ k , s ] < = m ( m 为 所 有 数 的 l c m ) 2、lcm[k,s] <= m(m为 所有数的lcm) 2、lcm[k,s]<=m(m为所有数的lcm)
对于上面第二条我们得知: k k k 与 s s s 一定是 m m m 的约数,因为它们的最小公倍数是 m m m !
故预处理出 m m m 的所有约数,然后枚举 k 和 s k 和 s k和s 的时候,时间复杂度就会少许多。
然后就是还要预处理一下,在1000范围内的数,所有数的最小公倍数,因为之后 d p dp dp 会用到。
代码如下:
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int lcm[1008][1008];
int dp[2][1008][1008];
int fac[1008];
int n, m, k, cnt;
const int mod = (int)(1e9 + 7);
int gcd(int a, int b)
{
if (b)
return gcd(b, a%b);
return a;
}
int main()
{
for (int i = 1; i <= 1000; i++) {
for (int j = i; j <= 1000; j++) {
lcm[i][j] = lcm[j][i] = (i * j) / gcd(i, j);
}
}
while (~scanf("%d%d%d", &n, &m, &k))
{
cnt = 0;
for (int i = 1; i <= m; i++) {
if (m%i == 0) fac[++cnt] = i;
}
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= cnt; i++) {
dp[0][fac[i]][fac[i]] = 1;
}
int g = 0, t, tt, l;
for (int i = 1; i < k; i++) {
memset(dp[g ^ 1], 0, sizeof(dp[g ^ 1]));
for (int j = i; j < n; j++) {
for (int w = 1; w <= cnt; w++) {
t = fac[w];
if (j + t > n) break;
for (int q = 1; q <= cnt; q++) {
tt = fac[q];
if (!dp[g][j][tt]) continue;
l = lcm[t][tt];
if (l > m) continue;
dp[g ^ 1][j + t][l] += dp[g][j][tt];
dp[g ^ 1][j+t][l] %= mod;
}
}
/*for (int w = 1; w <= cnt; w++) {
int t = fac[w];
if (!dp[g][j][t]) continue;
for (int q = 1; q <= cnt; q++) {
int tt = j + fac[q];
if (tt > n) break;
int l = lcm[t][fac[q]];
dp[g ^ 1][tt][l] += dp[g][j][t];
dp[g ^ 1][tt][l] %= mod;
}
}*/
}
g ^= 1;
}
printf("%d\n", dp[g][n][m]);
}
}