动态规划用逆序法求解0/1背包问题

求解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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值