【问题】
给定n种物品和一个背包,物品i(1≤i≤n)的重量是,其价值为,背包容量为,对于每种物品只有两种选择:装入背包或者不装入背包。如何选择装入背包的物品,使得装入背包中物品的总价值最大?
【想法】
首先证明0/1背包问题满足最优性原理。
设是0/1背包问题的最优解,则是下面子问题的最优解:
其中要找到
如若不是子问题最优解,则在子问题必然有一个最优解的前提下,设是上述子问题的一个最优解,则,且。因此,,这说明是0/1背包问题的最优解且比更优,从而导致矛盾。
用0、1表示的装入与否,设表示将n个物品选择性装入容量为C的背包中所获得的最大值。显然,初始子问题是把前面i个物品装入容量为0的背包和把0个物品装入到容量为j的背包中,此时的V值均是0
我们在选择装入时通常遇到两种情况:
一是背包容量足够大,此时自然是装入的物品越多价值越大(尽管没有这样的背包);
二是背包容量有限,我们需要对n件物品进行筛选,选择性价比最高的一种装入方法,而且背包的容量要接近100%使用。
那么会有一下两种状况:
- 当我们装到一定程度时,发现背包剩余容量不足以装入第i件物品,那么选择装入第i件物品是的最大价值与选择装入第i-1件物品的最大价值是相同的;
- 背包容量可以装第i件物品,那么装入背包价值等于一个拥有容量的背包在i-1件物品中选择是否装入+第i个物品的价值;如果没有装入第i个物品,那么背包中物品的价值等于一个容量为j的背包在i-1件物品中选择装入的价值;显然取二者中价值最大的作为最优解。于是有以下递推式;
接着要确定背包装入了哪些物品,得从后往前推,如果V(n,C)>V(n-1,C),表明第n个物品被装入,将第n个物品踢出,得到一个容量为的背包选择n-1个物品;否则则是一个容量为C的背包选择n-1个物品,以此类推,直到确定第一个物品的装入与否。由此,得到如下函数:
例如,有5个物品,其重量分别是{2,2,6,5,4},价值分别为{6,3,5,4,6},背包容量为10,用动态规划法求解0/1背包问题,过程如图表所示(动态规划法的核心就是建立图表)
【算法实现】
#include<iostream>
using namespace std;
#include<cmath>
#include<string>
int V[6][11];
int x[5];
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
int KnapSack(int w[],int v[],int n,int C)
{
int i,j;
for(i=0;i<=n;i++)//初始化第0列
V[i][0]=0;
for(j=0;j<=C;j++)//初始化第0行
V[0][j]=0;
for(i=1;i<=n;i++)//计算第i行,进行第i次迭代
{
for(j=1;j<=C;j++)
{
if(j<w[i-1])
V[i][j]=V[i-1][j];
else
V[i][j]=max(V[i-1][j],V[i-1][j-w[i-1]]+v[i-1]);
}
}
for(i=n,j=C;i>0;i--)//求装入背包的物品
{
if(V[i][j]>V[i-1][j])
{
x[i-1]=1;
j=j-w[i-1];
}
else
x[i-1]=0;
}
return V[n][C];//返回背包取得的最大价值
}
int main()
{
int w[5]={2,2,6,5,4};
int v[5]={6,3,5,4,6};
cout<<"背包最大价值是:"<<KnapSack(w,v,5,10)<<endl;
cout<<"装入的物品分别是:";
for(int i=0;i<5;i++)
{
if(x[i]==1)
cout<<"物品"<<i+1<<'\t';
}
return 0;
}
【输出结果】
【结语】
第一次写文章,所以不是太懂怎么使用编辑器,所以先在Word里先写了初稿,再来编辑这篇文章!对于编辑的方法还有很多没掌握,数学公式编辑器我用的是Math Type,很多东西编辑出来没有预想的结果,希望大家海涵!另外由于本人水平有限,有错误之地还望大家指正批评!
同时在此祝大家五一劳动节快乐!