soj 3531 Number Pyramids(观察组合数 + 每种物品至少选一个的完全背包)


题意
给你一个类似杨辉三角的堆积法,问在给定顶端的数和,底部长度的情况下,一共有多少种堆积方式?
分析
这个拿到找了半天分解子问题,记忆化搜索都没有办法…最后才发现了最底部的数,会被扩大,然后传递到顶部去,然后观察这个扩大倍数或发现恰恰是组合数!
假设最底部的数是: a[1],a[2],....a[n] 那么最上面的数就是

C0n1a[1]+C1n1a[2]+.....+Cnn1a[n]

然后就把这些组合数当做物品,他们的系数 a[i] 当做选择的物品的个数来做完全背包就可以了.
然后这里比较特殊的是,每种物品都要至少选择一个,我们就先把背包总容量减去每个物品选一个所需要消耗的容量( i=1...na[i] ),剩下的容量再来做完全背包.定义dp[i]为要达到这个容量有多少种选择方式,显然 dp[i]+=dp[iCin] ,


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值