问题概述:
有N件物品和一个容量为V的背包。第 i 件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大,求最大价值。(1≤n≤20)
代码一:
(DFS:递归遍历,每种物品选和不选的两种结果,所有情况都遍历)
#include<cstdio>
using namespace std;
const int maxn=30;
int n,v,maxvalue=0;//物体的个数,背包容量,最大价值
int w[maxn],c[maxn];//每件物品的重量和价值
void dfs(int index,int sumw,int sumc){//当前处理的物品编号, 当前总重量,当前总价值
if(index==n){//出口条件为遍历完n件商品
if(sumw<=v&&sumc>maxvalue){
maxvalue=sumc;//满足条件时更新最大价值
}
return; //结束
}
dfs(index+1,sumw,sumc);//不选第index件物品
dfs(index+1,sumw+w[index],sumc+c[index]);//选择第index件物品
}
int main()
{
scanf("%d%d",&n,&v);//物品个数和背包容量
for(int i=0;i<n;i++){
scanf("%d",&w[i]);
}
for(int i=0;i<n;i++){
scanf("%d",&c[i]);
}
dfs(0,0,0);//当前的物品编号为0,总质量和总价值均为0
printf("%d\n",maxvalue);
return 0;
}
代码二:
1、代码一的进化版(节省了部分计算量)。
2、同样是DFS,但当目前总重量超过背包最大容量时,不再继续遍历(俗称“剪枝”)。
#include<cstdio>
using namespace std;
const int maxn=30;
int n,v,maxvalue=0;//物体的个数,背包容量,最大价值
int w[maxn],c[maxn];//每件物品的重量和价值
void dfs(int index,int sumw,int sumc) //当前处理的物品编号, 当前总重量,当前总价值
{
if(index==n) //出口条件为遍历完n件商品 (能到达这里的一定是符合条件的)
{
return;
}
dfs(index+1,sumw,sumc);//不选第index件物品 (不用判断背包是否可容纳)
if(sumw+w[index]<=v)
{
if(sumc+c[index]>=maxvalue)
{
maxvalue=sumc+c[index];//更新最大价值(最大价值不一定每次更新,当前物品重量为0时不更新)
}
dfs(index+1,sumw+w[index],sumc+c[index]);//选择第index件物品
}
}
int main()
{
scanf("%d%d",&n,&v);//物品个数和背包容量
for(int i=0; i<n; i++)
{
scanf("%d",&w[i]);
}
for(int i=0; i<n; i++)
{
scanf("%d",&c[i]);
}
dfs(0,0,0);//当前的物品编号为0,总质量和总价值均为0
printf("%d\n",maxvalue);
return 0;
}
代码三:
(动态规划方法,时间复杂度和空间复杂度均为O(nV))
#include<bits/stdc++.h>
#define maxn 1000
using namespace std;
int dp[maxn][maxn];//dp[i][v]表示前i件物品恰好装入容量为v的背包中所能获得的最大价值
int main()
{
int n,V,w[maxn],c[maxn];//物品个数 背包总容量 每件物品的重量 每个物品的价值
scanf("%d%d",&n,&V);
for(int i=0; i<n; i++)
{
scanf("%d",&w[i]);
}
for(int i=0; i<n; i++)
{
scanf("%d",&c[i]);
}
for(int v=0; v<=V; v++)
{
dp[0][v]=0;//边界(即前0件物品放入任何容量为v的背包里都只能获得价值0
}
for(int i=1; i<=n; i++)
{
for(int v=w[i]; v<=V; v++)
{
//不放第i件物品,那么问题转化成前i-1件物品 恰好装入容量为v的背包中所能获得的最大价值,即 dp[i-1][v]
//放第i件物品,么问题转化成前i-1件物品 恰好装入容量为v-w[i]的背包中所能获得的最大价值 ,即dp[i-1][v-w[i]]+c[i]
dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i]);
}
}
printf("%d\n",dp[n][V]);
return 0;
}
代码四:
(代码三的进化版,动态规划使用一维数组,空间复杂度可以达到O(V))
#include<bits/stdc++.h>
#define maxn 1000
using namespace std;
int dp[maxn];
int main()
{
int n,V,w[maxn],c[maxn];//物品个数 背包总容量 每件物品的重量 每个物品的价值
scanf("%d%d",&n,&V);
for(int i=0; i<n; i++)
{
scanf("%d",&w[i]);
}
for(int i=0; i<n; i++)
{
scanf("%d",&c[i]);
}
for(int v=0; v<=V; v++)
{
dp[v]=0;//边界
}
for(int i=1; i<=n; i++)
{
for(int v=V; v>=w[i]; v--)//一维数组必须逆序
{
dp[v]=max(dp[v],dp[v-w[i]]+c[i]);
}
}
int ans=0 ;
for(int v=0;v<=V;v++){
if(dp[v]>ans){
ans=dp[v];
}
}
printf("%d\n",ans);
return 0;
}