内容会持续更新,有错误的地方欢迎指正,谢谢!
问题
有 N 件物品和一个容量为 V 的背包。第 i 件物品的体积是 Ci,其价值是 Wi。求在不超过背包容量V的情况下,将哪些物品装入背包可使价值总和最大。
思路
很明显的一道考察动态规划的题目。
F(i,V)表示 在前i件商品中选择若干件放入容量为V的背包中,可获得的最大价值。
递推式:
1. 如果i等于0,F(i,V)=0
2. 如果i大于0,F(i,V)=max{F(i-1,V),F(i-1,V-Ci)+Wi}
即对于第i件物品,有要与不要两种选择:如果不要,F(i,V)=F(i-1,V);如果要,那就先判断V-Ci是否>=0确保有没有足够的空间,如果没有那就还是不要,如果有那就要F(i,V)=F(i-1,V-Ci)+Wi。
代码
//递归实现
#include <iostream>
using namespace std;
int C[5]={5,1,4,2,1};//每个物品的体积
int W[5]={4,1,5,4,2};//每个物品的价值
int MaxValue(int i,int V)
{
if(i==0||V==0)//没有物品可选或体积为0时
return 0;
int doChoose=-1;
if(V-C[i-1]>=0)//i的取值范围为[1,6],所以要减一
doChoose=MaxValue(i-1,V-C[i-1])+W[i-1];
int notChoose=MaxValue(i-1,V);
return (doChoose>notChoose)?doChoose:notChoose;
}
int main()
{
int N=5;//物品的件数
int V=8;//背包的体积
cout << MaxValue(N,V) << endl;
return 0;
}
//迭代实现,F这个二维数组就是 价值F(i,V) 的表格,最右下角的元素为最大价值。
#include <iostream>
using namespace std;
int main()
{
const int N = 6;//物品个数
const int V = 10;//背包体积
int C[N+1] = {0,5,1,4,2,1};//每个物品的体积,下标从1开始
int W[N+1] = {0,4,1,5,4,2};//每个物品的价值
int F[N + 1][V + 1] = { 0 };//全部初始化为0,用于记录状态
for (int i = 1; i <= N; i++)//对于第 i 个物品
for (int v = 0; v <= V; v++)
{
F[i][v] = F[i - 1][v];//第i个不放
//如果放了第i个,比它大的话,就放第i个
if (v - C[i] >= 0 && F[i][v] < F[i - 1][v - C[i]] + W[i])
F[i][v] = F[i - 1][v - C[i]] + W[i];
}
cout << "最大价值是:" << F[N][V] << endl;
return 0;
}
复杂度分析及优化
对于迭代这个方法来说,时间和空间复杂度均为 O(VN),其中时间复杂度应该已经不能再优化了,但空间复杂度却可以优化到 O(V)。
以v递减顺序计算 F[v],这样才能保证计算 F[v] 时 F[v−Ci] 保存的是状态F[i−1,v−Ci] 的值。
//了解就好
#include <iostream>
using namespace std;
int main()
{
const int N = 6;//物品个数
const int V = 8;//背包体积
int C[N+1] = {0,5,1,4,2,1};//每个物品的体积,下标从1开始
int W[N+1] = {0,4,1,5,4,2};//每个物品的价值
int F[V + 1] = { 0 };
for (int i = 1; i <= N; i++)//对于第 i 个物品
for (int v = V; v-C[i]>=0; v--)
F[v]=max(F[v],F[v-C[i]]+W[i]);
cout << "最大价值是:" << F[V] << endl;
return 0;
}
如上,空间复杂度便得到了优化,变成了O(V)