一、Bone Collector
Problem Description
许多年前,在泰迪的家乡,有一个人被称为“骨收集者”。骨头收集者有一个大袋子,里面装满了V,在收集骨头的过程中,不同的骨骼具有不同的值和不同的体积,现在给定每个骨骼的值,您能否计算出骨骼收集器可获得的总值的最大值?
Input
第一行包含整数T,即案例数。
紧随其后的是T个案例,每个案例三行,第一行包含两个整数N,C(N <= 1000,C <= 1000),
表示骨头的数量和包的体积。第二行包含代表每个骨骼值的N个整数。第三行包含N个整数,代表每个骨骼的体积。
Output
每行一个整数,代表总和的最大值(该数字将小于2^31^)。
Sample Input
1 5 10 1 2 3 4 5 5 4 3 2 1
Sample Output
14
解法一:二维数组解法(0/1背包模板代码)
tip:注意多组数据要清dp数组为零,忘记了所以wa了一次。
一道模板题~ 根据上次讲解的0/1背包推导公式(0/1背包讲解戳这里~.),并用条件语句实现公式即可。
二维表是N+1行(第0行是前0个物品的情况,也就是没有东西可以装),
C+1(第0列是背包0容量的情况)。
第0行和第0列的dp数组值都是0,其他根据公式填入,最大(最优)的情况就是二维数组的最右下角那个格子的值即dp[N][C]。
#include<bits/stdc++.h> #define fio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; const int maxn = 1e3+1; int N, C; int dp[maxn][maxn]; struct { int v; int n; }node[maxn]; void back() { for (int i = 0; i <= N; i++) dp[i][0] = 0; for (int i = 1; i <= N; i++) { for (int j = 0; j <= C; j++) { if (node[i].n > j) { //第i个物品太大装不下 dp[i][j] = dp[i-1][j]; } else {//第i个物品可以装,选择装或不装,选择两种情况结果最大的。 dp[i][j] = max(dp[i-1][j-node[i].n]+node[i].v, dp[i-1][j]); } } } } int main() { fio int T; cin >> T; while (T--) { cin >> N >> C; memset(dp, 0, sizeof(dp)); for (int i = 1; i <= N; i++) cin >> node[i].v; for (int i = 1; i <= N; i++) cin >> node[i].n; back(); cout << dp[N][C] << endl; } }
1.1 0/1背包打印方案代码
判断所填的值dp[i][j]是等于dp[i-1][j-ni]+vi还是dp[i-1][j]
用条件语句直接判别两种情况:
若等于dp[i-1][j-n
i]+vi,则a[i]=1,表明选了第i个物品,j更新为j-ni。若等于dp[i-1][j],a[i]=0(不用赋值,已初始化),表明没选第i个物品,j不更新。
两种情况 i 都要减1。
int a[maxn];//记录解的数组 void output() { int i = N, j = C; int r = N; while (r--) { if (dp[i][j] == (dp[i-1][j-node[i].n]+node[i].v)) { j = j-node[i].n; a[i] = 1; } i -= 1; } for (int i = 1; i <= N; i++) if (a[i]) cout << i << " "; }
解法二:滚动数组(一维)解法
适合:N、C很大的情况
不适合:打印具体方案
把二维dp[][]变成一维的dp[],这个解放可以节省更多的空间。二维dp[][]中每一行是由上一行计算出来的,所以只跟上一行有关。
故我们可以直接用新的一行覆盖原来一行。
int dp[1001]; void back() { memset(dp, 0, sizeof(dp)); for (int i = 1; i <= N; i++) for (int j = C; j >= node[i].n; j--) dp[j] = max(dp[j], dp[j-node[i].n]+node[i].v); cout << dp[C] << endl; }
初始化为0,第一次dp是从物品1开始,倒着从最大容量往物品的重量的方向依次填入值(也就是图中以5为分界,5前面的位置填原来的值,不更新;5后面的值计算填入),倒着计算可以省掉前面几个不必计算的空格,减少计算量。
重复上述覆盖操作,即可得到第五次dp的结果,取dp[N]即是最优解。