有 k 类课程,第 i 类学分为 a[i],有 b[i] 门可选。求选总学分恰为 n 的组合数(同学分课程视为无差别)。约束:n≤40, k≤8, a≤8, b≤10。
dp[j] 表示凑成学分 j 的组合数。初始 dp[0]=1。对每类课程,做多重背包转移。
#include <cstdio>
#include <cstring>
#define MAX 55
int dp[MAX];
int main() {
int T; scanf("%d", &T);
while (T--) {
int n, k; scanf("%d%d", &n, &k);
memset(dp, 0, sizeof(int)*(n+10));
dp[0] = 1;
for (int i = 0; i < k; ++i) {
int a, b; scanf("%d%d", &a, &b);
for (int j = n; j >= a; --j)
for (int x = 1; x <= b; ++x)
if (j >= x*a) dp[j] += dp[j - x*a];
}
printf("%d\n", dp[n]);
}
return 0;
}
void zeroOnePack(int v, int V) { for(int i=V;i>=v;--i) dp[i]+=dp[i-v]; }
void completePack(int v, int V) { for(int i=v;i<=V;++i) dp[i]+=dp[i-v]; }
void multiplePack(int v, int cnt, int V) {
if (cnt * v > V) completePack(v, V);
else {
int k = 1;
while (k <= cnt) { zeroOnePack(k*v, V); k <<= 1; }
if (cnt > 0) zeroOnePack(cnt*v, V); // 剩余部分
}
}
错误原因:二进制拆分逻辑有误,且组合数计数与最值问题不同,不能直接套用二进制优化(会重复计数)。
对于计数问题,二进制拆分不适用(顺序敏感)。正确做法:
每类课程贡献多项式 (1 + x^a + x^{2a} + ... + x^{b·a}),求乘积中 x^n 系数。DP 本质即多项式乘法。