一、题目链接
https://nanti.jisuanke.com/t/31720
二、题意
给$N$种船只,第$i$种船的载重量是$V_i$,数量是$2^{C_i}-1$。接下来有$Q$次询问,每次给定一个数字$S(1 \le S \le 10000)$,表示货物重量,回答有多少种载货方案。注意,每条船要么不载货,要么满载。
三、思路
比较裸的多重背包。如果写最朴素的多重背包$dp$,时间复杂度是$O(N*10000*2^{C_i})$,显然会超时。
所以,考虑二进制优化。做法是,设$C_i$的二进制表示为$s$,如果$s[i]=1$($i$从$0$开始数,从右边开始),那么,把这$2^i$条船合并成一条,载货量是$2^i*V_i$。
然后就是01背包计数了。
四、代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; const LL mod = 1000000007; int v[510], n, q, ncnt; LL dp[510][10010]; int main() { int T, x, y; for(scanf("%d", &T); T--;) { ncnt = 0; scanf("%d%d", &n, &q); for(int i = 1; i <= n; ++i) { scanf("%d%d", &x, &y); y = (1 << y) - 1; for(int i = 1; i <= y; i <<= 1) { v[++ncnt] = i * x; } } for(int i = 0; i <= ncnt; ++i) { for(int j = 0; j <= 10000; ++j)dp[i][j] = 0; } dp[0][0] = 1; for(int i = 1; i <= ncnt; ++i) { for(int j = 0; j <= 10000; ++j) { dp[i][j] = dp[i - 1][j]; if(j >= v[i])dp[i][j] = (dp[i][j] + dp[i - 1][j - v[i]]) % mod; } } while(q--) { scanf("%d", &x); printf("%lld\n", dp[ncnt][x]); } } return 0; }