题目描述
有 n 件物品,每件物品有一个重量和一个价值,分别记为 w1,w2,…,wn 和 c1,c2,…,cn。现在有一个背包,其容量为 wk,要从 n 件物品种任取若干件。要求:(1)重量之和小于或等于 wk;(2)价值之和最大。
输入:
共 3 行,第一行 2 个整数,表示 n 和 wk;第二行 n 个整数表示每一个物品的重量,第三行 n 个整数表示每一个物品的价值。
输出:
一行一个整数,表示符合背包容量的最大价值。
样例:
8 200
79 58 86 11 28 62 15 68
83 14 54 79 72 52 48 62
暴力枚举
我们以只有 A、B、C 三件物品的情况为例,对于每一个物品都存在拿
和不拿
两种情况。以0
表示不拿当前物品,以1
表示拿当前物品,可以有如下分析结果。
可能上面的图看起来不够清晰,我们从左至右逐一列举出来观察,一眼就可以看出来规律。其实就是十进制的 0、1、2、3、4、…可枚举的最大值即 2n-1。
000
001
010
011
100
101
110
111
根据上面的分析,我们可以写出如下代码。
#include<bits/stdc++.h>
using namespace std;int main()
{int n, wk;int w[10000], c[10000];cin>>n>>wk;for(int i = 0; i < n; i++){cin>>w[i];}for(int i = 0; i < n; i++){cin>>c[i];}int ans = 0;int max_val = 1 << n;// 逐一枚举for(int i = 0; i < max_val; i++){int ww = 0, cc = 0;int index = 0;// 转二进制int cur = i;while(cur){int bit = cur % 2;// 若拿第 index 个物品,则累加其重量和价值if(bit){ww += w[index];cc += c[index];}cur = cur >> 1;index++;}//计算最大值if(ww <= wk && ans < cc){ans = cc;}}//输出最大值cout<<ans<<endl;
}
递归求解
我们把背包容量为wk
,有n
个物品可以选择的问题表示为slove(wk, n)
。那么在背包剩余容量可以装下第 n 个物品时,该问题可以表示为求如下两个问题的最大值
- 选第 n 个物品:
c[n-1] + slove(wk-w[n-1], n-1)
- 不选第 n 个物品:
slove(wk, n-1)
在背包剩余容量无法装下第 n 个物品时,问题直接变为
- 不选第 n 个物品:
slove(wk, n-1)
可以发现上述三个子问题可以继续向下拆分为规模更小,但类型一致的子子问题。于是可以写出如下递归求解代码。
#include<bits/stdc++.h>
using namespace std;int w[30]={0}, c[30]={0};// wk 背包剩余重量
// ch 可选项
int slove(int wk, int ch)
{if(wk <= 0 || ch <= 0){return 0;}// 若背包剩余容量无法装下 w[ch-1],则直接丢弃第 ch 个物品if(w[ch-1] > wk){return slove(wk, ch-1);}// 若背包剩余容量能装下 w[ch-1],则计算装和不装的最大值int a = c[ch-1] + slove(wk-w[ch-1],ch-1);int b = slove(wk, ch-1);return a > b ? a : b;
}int main()
{int n, wk;cin>>n>>wk;for(int i = 0; i < n; i++){cin>>w[i];}for(int i = 0; i < n; i++){cin>>c[i];}cout<<slove(wk, n);
}
动态规划
递归在执行过程中会存在重复计算相同子问题的情况,我们可以将其改为用循环实现,即动态规划的写法。dp[i][j]
的含义即为:在背包容量为i
,可选物品数量为j
的情况下,符合背包容量的最大值。具体代码如下所示:
#include<bits/stdc++.h>
using namespace std;int w[30]={0}, c[30]={0};int main()
{int n, wk;cin>>n>>wk;for(int i = 0; i < n; i++){cin>>w[i];}for(int i = 0; i < n; i++){cin>>c[i];}int dp[1000001][21] = { 0 };for(int i = 1; i <= wk; i++) {for(int j = 1; j <= n; j++) {// 若背包剩余容量无法装下 w[j-1],则直接丢弃第 j 个物品if(w[j-1] > i) {dp[i][j] = dp[i][j-1];} else {// 若背包剩余容量能装下 w[j-1],则计算装和不装的最大值int a = c[j-1] + dp[i-w[j-1]][j-1];int b = dp[i][j-1];dp[i][j] = a > b ? a : b;}}}cout<<dp[wk][n];
}