题目大意:
这个问题和 01背包 问题很相似,我们也可以依然采取 01背包 的状态定义
dp[i][j] 代表前 i 个物品 容量为 j 的背包的最大价值
那么状态转移方程也就出来了:
dp[i][j] = max(dp[i][j],dp[i-1][j-k*v[i]]+w[i]) (k 可以为 0,1,2,3...s[i])
int dp[110][110],v[110],w[110],s[110]; int main() { int n,m; cin >> n >> m; for (int i = 1;i <= n;i++) { cin >> v[i] >> w[i] >> s[i]; } for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) { for (int k = 0;k <= s[i];k++) { if (k * v[i] <= j) dp[i][j] = std::max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]); } } } cout << dp[n][m] << endl; return 0; }
这种朴素的方法的时间复杂度 和 空间复杂度都很高,所以一般不采取
我们可以先采取和 01背包 一样的滚动数组的方式去优化一下
简单优化:
和 01背包 一样,我们也应该从大往小更新,理由和 01背包 也是一样的,这里就不多说了
int dp[1010]; int main() { int n,m; std::cin >> n >> m; for (int i = 1;i <= n;i++) { int v,w,s; std::cin >> v >> w >> s; for (int j = m;j >= 0;j--) { for (int k = 1;k <= s && k * v <= j;k++) { dp[j] = std::max(dp[j],dp[j-k*v]+k*w); } } } std::cout << dp[m] << std::endl; return 0; }
二进制优化:拆解成多个 01背包问题
其实就是把 件数s 进行二进制的拆分 ,因为这样拆分之后的数 可以表示 [0~s] 之间的任何数 (自行证明)
int dp[2010]; struct Node { int v,w; }; std::vector<Node> vec; int main() { int n,m; std::cin >> n >> m; for (int i = 1;i <= n;i++) { int v,w,s; std::cin >> v >> w >> s; for (int k = 1;k <= s;k <<= 1) { s -= k; vec.push_back({k*v,k*w}); } if (s > 0) vec.push_back({s*v,s*w}); } for (auto i:vec) { for (int j = m;j >= i.v;j--) { dp[j] = std::max(dp[j],dp[j-i.v]+i.w); } } std::cout << dp[m] << std::endl; return 0; }
这个时候的算法复杂度以及足够优秀了 ,当然还有一种更加优秀的做法就是 采取单调队列优化 (自行搜索)
来源:https://www.cnblogs.com/-Ackerman/p/12250417.html