0/1背包
给出(n)个物品,每个物品(i)的价值为(v_i),重量为(w_i),背包的总容量为(C),如何放置物品可以使得背包所装的物品的价值总和最大,对于每种物品只有装和不装两个选择,不存在装部分的情况,另外每种物品的数量无限.
题目链接
想法
从第一个物品开始,背包的容量从0
到V
开始迭代进行计算,如果当前物品的重量大于此时的容量,那么这个物品就不用放入背包,此时背包可以容纳的最大价值的物品的最优选择是上一轮迭代做出的,即dp[i][j] = dp[i-1][j]
.当此时背包的容量比这个物品大时有两种选择,一种是不放入这个物品,另外一种是放入这个物品,背包有效的容量变小,此时背包内物品的价值是这个物品和去掉这个物品后的背包容量可以容纳的最大价值之和,从这两个选择之中取最大的.
最优解方案的打印,再每一种物品的迭代选择中,每种物品只有选择和不选择两种状态,所以在当选择某个物品的时候要标记选择这个物品,打印方案时,从后向前打印.对于dp[i][j]
,先判断path[i][j]
是不是1
,如果是,那么打印输出i
并且从j
中同时减去物品i
的重量,输出下一个物品;如果path[i][j]
是0
,那么说明第i
个物品没有被选中,dp[i][j]
的最优解是从前i-1
个物品中得到的,所以让i
减一,继续进行迭代,直到`i==0``判断完了所有物品的选择,没经过一步迭代,一定可以判断一个物品有没有被选择.
为了输出方案,需要保存中间每种物品迭代时的状态,会造成较大的空间占用,当输出不要求方案,只要求输出最优解时尅通过滚动数组减少空间的消耗.用滚动数组时注意,外层的循环是正向迭代每种物品,内层循环从后向前滚动,之所以这样,因为此时的dp[j-nodes[i].cost]
是上一轮迭代计算得到的,然后在本轮计算中会更新,正向算的话会先把上一轮计算的覆盖掉,因为此时的dp[j-nodes[i].cost]
是在第i
件物品下的最优选择了,不再是第i-1
件时的最优选择,而dp[j]
的对于第i
件物品选择时要考虑dp[j-nodes[i].cost]
在第i-1
时的最值,所以必须逆序滚动.
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
const int M = 1005;
struct node{
int value;
int cost;
};
ll dp[N][M] = {0};
node nodes[N] = {0};
bool path[N][M] = {0};
void solve(int n, int c){
memset(dp, 0, sizeof(dp));
memset(path, 0, sizeof(path));
// dp[0][0] = 0;
for (int i = 1; i <= n; ++i){
for(int j = 0; j <= c; ++j){
if(nodes[i].cost > j){
dp[i][j] = dp[i - 1][j];
path[i][j] = 0;
}else{
// dp[i][j] = max(dp[i-1][j], dp[i-1][j-nodes[i].cost]+nodes[i].value);
if( dp[i-1][j] > dp[i-1][j-nodes[i].cost]+nodes[i].value){
dp[i][j] = dp[i-1][j];
path[i][j] = 0;
}else{
dp[i][j] = dp[i - 1][j - nodes[i].cost] + nodes[i].value;
path[i][j] = 1;
}
}
}
}
}
void print(int n, int c){
while( n > 0 ){
if( path[n][c]){
printf("%d ", n);
c -= nodes[n].cost;
}
--n;
}
printf("
");
}
int main(int argc, const char** argv) {
int t;
scanf("%d", &t);
while(t--){
int n, c;
scanf("%d%d", &n, &c);
for(int i = 1; i <= n; ++i){
scanf("%d", &nodes[i].value);
}
for(int i = 1; i <= n; ++i){
scanf("%d", &nodes[i].cost);
}
solve(n, c);
printf("%lld
", dp[n][c]);
// print(n, c);
}
return 0;
}
// 滚动数组
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
struct node{
int value;
int cost;
};
ll dp[N];
node nodes[N] = {0};
void solve(int n,int m){
memset(dp, 0, sizeof(dp));
for(int i = 1; i <=n; ++i){
for (int j = m; j >= nodes[i].cost; --j){
dp[j] = max(dp[j],dp[j-nodes[i].cost]+nodes[i].value);
}
}
printf("%lld
", dp[m]);
}
int main(int argc, const char** argv) {
int t;
scanf("%d", &t);
while(t--){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i){
scanf("%d", &nodes[i].value);
}
for(int i = 1; i <= n; ++i){
scanf("%d", &nodes[i].cost);
}
solve(n, m);
}
return 0;
}