01背包
题目
有N件物品和一个容量为V的背包。第i件物品的重量是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
物品 | 1 | 2 | 3 | 4 | 5 |
w | 3 | 2 | 4 | 1 | 6 |
v | 4 | 5 | 7 | 6 | 2 |
思路
这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
将前i件物品放入容量为j的背包中,只考虑第i件物品(放或不放),那么前i件物品能获得的最大价值由前i-1件物品能获得的最大值有关。
如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为dp[i-1][j];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-w[i]的背包中”,此时能获得的最大价值就是p[i-1][j-w[i]]再加上通过放入第i件物品获得的价值v[i]。
用子问题定义状态:即f[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。
二维状态转移方程是:dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i])。
一维状态转移方程是:dp[j]=max(dp[j],dp[j-w[i]]+v[i])。我在计算dp(i, j)的时候,因为dp(i, j+1..kg)这些状态已经被计算过了,所以意味着dp(i - 1, k),k=j..kg这些值都没有用了——所有依赖于他们的值都已经计算完了。于是它们原有的存储空间都可以用来存储别的东西,所以我就直接就将dp(i, j)的值存在dp(i-1, j)原有的位置上
如果只求最大价值:则下面第一个代码就可以,若还需输出哪些物品被选了,则加一个take[]数组,记录是否被选,见第二个代码
只求最大价值,二维存储
#include<stdio.h>
#define max(a,b) a>b?a:b
int w[100],v[100],dp[100][100],n,kg;
void dpfind(){
int i,j;
for(i=0;i<=n;i++){//背包不能装物品
dp[i][0]=0;
}
for(i=0;i<=kg;i++){//背包一个物品也没装
dp[0][i]=0;
}
for(i=1;i<=n;i++){//前i个物品
for(j=1;j<=kg;j++){//容量为j的背包
if(j>=w[i]){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
}
}
int main(){
int i,j;
scanf("%d%d",&n,&kg);
for(i=1;i<=n;i++){//重量
scanf("%d",&w[i]);
}
for(i=1;i<=n;i++){//价值
scanf("%d",&v[i]);
}
dpfind();
printf("%d\n",dp[n][kg]);//输出最大价值
}
只求最大价值,一维存储
#include<stdio.h>
#include<string.h>
#define max(a,b) a>b?a:b
int w[100],v[100],dp[100],n,kg;
void dpfind(){
int i,j;
for(i=1;i<=n;i++){//前i个物品
for(j=kg;j>=w[i];j--){//容量为j的背包
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
}
int main(){
int i,j;
scanf("%d%d",&n,&kg);
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++){//重量
scanf("%d",&w[i]);
}
for(i=1;i<=n;i++){//价值
scanf("%d",&v[i]);
}
dpfind();
printf("%d\n",dp[kg]);//输出最大价值
}
findtake(){//判断是否被选
int i,j=kg;
for(i=n;i>0;i--){
if(dp[i][j]==dp[i-1][j]){
take[i]=0;
}
else{
take[i]=1;
j=j-w[i];
}
}
}
完整:
#include<stdio.h>
#define max(a,b) a>b?a:b
int w[100],v[100],dp[100][100],n,kg,take[100];
void dpfind(){
int i,j;
for(i=0;i<=n;i++){//背包不能装物品
dp[i][0]=0;
}
for(i=0;i<=kg;i++){//背包一个物品也没装
dp[0][i]=0;
}
for(i=1;i<=n;i++){//前i个物品
for(j=1;j<=kg;j++){//容量为j的背包
if(j>=w[i]){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
}
}
findtake(){//判断是否被选
int i,j=kg;
for(i=n;i>0;i--){
if(dp[i][j]==dp[i-1][j]){
take[i]=0;
}
else{
take[i]=1;
j=j-w[i];
}
}
}
int main(){
int i,j;
scanf("%d%d",&n,&kg);
for(i=1;i<=n;i++){//重量
scanf("%d",&w[i]);
}
for(i=1;i<=n;i++){//价值
scanf("%d",&v[i]);
}
dpfind();
printf("%d\n",dp[n][kg]);//输出最大价值
findtake();
for(i=1;i<=n;i++){//输出装入背包的物品
if(take[i]==1){
printf("%d ",i);
}
}
}