求解0/1背包问题
一、【问题描述】
有n个重量分别为{w1,w2,…,wn}的物品,它们的价值分别为{v1,v2,…,vn},给定一个容量为W的背包。
设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中,要求选中的物品不仅能够放到背包中,而且重量和为W具有最大的价值。
二、【问题求解】
最优方案所指的是装入物品的价值最高,即 v1x1+v2x2+…+vn*xn(其中xi取0或者1,取1表示选取物品i)取得最大值。
Ⅰ、
设置二维动态规划数组dp,dp[i][r]表示背包剩余容量为r(1<=r<=W),已考虑物品1、2、…、i(1<=i<=n)时背包装入物品的最优价值。可得出对应的状态转移方程如下
dp[i][0]=0(背包不能装入任何物品,总价值为0)
dp[0][r]=0(没有任何物品可装入,总价值为0)
dp[i][r]=dp[i-1][r] 当r<w[i]时,物品i放不下
dp[i][r]=MAX{dp[i-1][r],dp[i-1][r-w[i]]+v[i]}
在不放入和放入物品i之间选择最优解
因此dp[n][W]是0/1背包问题的最优解。
Ⅱ、
当dp数组计算出来后,推导出解向量x要从dp[n][W]开始
(1)若dp[i][r]!=dp[i-1][r],若dp[i][r]=dp[i-1][r-w[i]]+v[i],置x[i]=1,累计总价值 maxv+=v[i],递减剩余重量r=r-w[i]
(2)若dp[i][r]=dp[i-1][r] 当r<w[i]时,物品i放不下或者不放入物品i,置x[i]=0。
综上总结就是
dp[i][r]=dp[i-1][r] 当r<w[i]时,物品i放不下
dp[i][r]=MAX{dp[i-1][r],dp[i-1[r-w[i]]+v[i]}
在不放入和放入物品i之间选最优解
代码实现:
//求解0/1背包问题的算法
#include <stdio.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define MAXN 20
#define MAXW 100
int n=5,W=10;
int w[MAXN]={0,2,2,6,5,4};
int v[MAXN]={0,6,3,5,4,6};
int dp[MAXN][MAXW];
int x[MAXN];
int maxv;
void Knap()
{
int i,r;
for (i=0;i<=n;i++)
dp[i][0]=0;
for (r=0;r<=W;r++)
dp[0][r]=0;
for (i=1;i<=n;i++)
{ for (r=1;r<=W;r++)
if (r<w[i])
dp[i][r]=dp[i-1][r];
else
dp[i][r]=max(dp[i-1][r],dp[i-1][r-w[i]]+v[i]);
}
}
void Buildx()
{
int i=n,r=W;
maxv=0;
while (i>=0)
{
if (dp[i][r]!=dp[i-1][r])
{
x[i]=1;
maxv+=v[i];
r=r-w[i];
}
else
x[i]=0;
i--;
}
}
int main()
{
Knap();
Buildx();
printf(" 选取的物品: ");
for (int i=1;i<=n;i++)
if (x[i]==1)
printf("%d ",i);
printf("\n");
printf(" 总价值=%d\n",maxv);
return 0;
}