题意
给你一个类似杨辉三角的堆积法,问在给定顶端的数和,底部长度的情况下,一共有多少种堆积方式?
分析
这个拿到找了半天分解子问题,记忆化搜索都没有办法…最后才发现了最底部的数,会被扩大,然后传递到顶部去,然后观察这个扩大倍数或发现恰恰是组合数!
假设最底部的数是:
a[1],a[2],....a[n]
那么最上面的数就是
C0n−1a[1]+C1n−1a[2]+.....+Cnn−1a[n]
然后就把这些组合数当做物品,他们的系数 a[i] 当做选择的物品的个数来做完全背包就可以了.
然后这里比较特殊的是,每种物品都要至少选择一个,我们就先把背包总容量减去每个物品选一个所需要消耗的容量( ∑i=1...na[i] ),剩下的容量再来做完全背包.定义dp[i]为要达到这个容量有多少种选择方式,显然 dp[i]+=dp[i−Cin] ,
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int mod = 1e9 + 9, maxn = 1e6 +9;
int n, m;
int dp[maxn];
int main(void) {
while (~scanf("%d%d", &n, &m)) {
if (n > 20) {
puts("0");
continue;
}
int x = 1 << (n - 1), y = n - 1;
if (x > m) {
puts("0");
continue;
}
int c = 1;
for (int i = 0; i <= y; i++) {
m -= c;
c = c * (y - i) / (i + 1);
}
fill(dp, dp + m + 9, 0);
dp[0] = 1;
for (int i = 0, c = 1; i <= y; i++) {
for (int j = c; j <= m; j++) {
dp[j] = (dp[j] + dp[j - c]) % mod;
}
c = c * (y - i) / (i + 1);
}
printf("%d\n", dp[m] % mod);
}
return 0;
}