0-1背包问题(动态规划)

本文详细阐述了利用动态规划解决01背包问题的方法,包括递归式定义、时间与空间复杂度分析,以及如何通过逆推确定最优装载方案。通过实例代码展示了解决过程,最终实现优化空间复杂度的动态规划算法。
一 问题描述:
     有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
    所谓01背包,表示每一个物品只有一个,要么装入,要么不装入。

二 解决方案:
   考虑使用动态规划求解,定义一个递归式 opt[i][v] 表示前i个物品,在背包容量大小为v的情况下,最大的装载量。
     
opt[i][v] = max(opt[i-1][v] , opt[i-1][v-c[i]] + w[i]) 
   解释如下:
     opt[i-1][v] 表示第i件物品不装入背包中,而opt[i-1][v-c[i]] + w[i] 表示第i件物品装入背包中。

  花费如下:
     时间复杂度为O(V * N) ,空间复杂度为O(V * N)。时间复杂度已经无法优化,但是空间复杂度则可以进行优化。

     但必须将V 递减的方式进行遍历,即V.......0 的方式进行(其实可以V....c[i])。

knapsack:

1:只装入1个物品,确定在各种不同载重量的背包下,能够得到的最大价值      
2:装入2个物品,确定在各种不同载重量的背包下,能够得到的最大价值
。。。      
n:以此类推,装入n个物品,确定在各种不同载重量的背包下,能够得到的最大价值


printMaxValue:

确定装入背包的具体物品,从opt[n][m]向前逆推:
opt[n][m]>opt[n-1][m],则第n个物品被装入背包,且前n-1个物品被装入载重量为m-w[n]的背包中
否则,第n个物品没有装入背包,且前n-1个物品被装入载重量为m的背包中
以此类推,直到确定第一个物品是否被装入背包为止

使用二维数组opt求解:

#include<iostream>
#define MAX 20
#define max(a,b) a>b?a:b 
using namespace std;
int N,V;//物品数量和容量
int cost[MAX]; //费用
int value[MAX];  //价值
int opt[MAX][MAX];  //存放最大价值
int x[MAX];//x[i]为1表示第i个物品放入背包

int knapsack()//参数是物品数量和容量,X数组表示组成,F[N][V]表示最大价值
{
	memset(opt,0,sizeof(opt));//初始化opt为0 
	for(int i=1;i<=N;i++)//求最大价值
	{
		for (int j=1;j<=V;j++)
		{
			if (cost[i]<=j)
				opt[i][j]=max(value[i]+opt[i-1][j-cost[i]],opt[i-1][j]);
			else
		 		opt[i][j]=opt[i-1][j];
		}
	}
	return opt[N][V];//返回最大值
}
void printMaxValue()//输出opt中的值(本函数无实际作用,只是为了显示下数据)
{
	for(int i=0;i<=N;i++)
	{
		for(int j=0;j<=V;j++)
			cout<<opt[i][j]<<" ";
		cout<<endl;
	}
} 
void traceback()//输出放入背包的物品编号 
{
	for(int i=N;i>0;i--)//x[i]为1表示第i个物品放入背包 
	{
		if(opt[i][V]==opt[i-1][V])
			x[i]=0;
		else
		{
			x[i]=1;
			V-=cost[i];
		}
	}	
	for(int i=0;i<=N;i++)
		if(x[i]) cout<<i<<"  ";
}

int main()
{
	while(cin>>N>>V)//输入物品数量和背包容量 
	{
		for(int i=1;i<=N;i++)
			cin>>cost[i]>>value[i];
		cout<<"max="<<knapsack2()<<endl;
		printMaxValue();
		traceback();
		cout<<endl;
	}
	return 0;
}


优化空间复杂度为O(V),使用一维数组:

#include<iostream>
#define MAX 20
using namespace std;
int N,V;//物品数量和容量
int cost[MAX]; //费用
int value[MAX];  //价值
int opt[MAX];  //存放价值

int knapsack()  //参数是物品数量和容量,X数组表示组成,F[N][V]表示最大价值
{
	memset(opt,0,sizeof(opt));//初始化opt为0 
	for(int i=1;i<=N;i++)//求最大价值
	{
		for (int j=V;j>=cost[i];j--)
		{
			if (value[i]+opt[j-cost[i]]>opt[j])
		 		opt[j]=value[i]+opt[j-cost[i]];
		}
	}
	return opt[V];//返回最大值
}
int main()
{
	while(cin>>N>>V)//输入物品数量和背包容量 
	{
		for(int i=1;i<=N;i++)
			cin>>cost[i]>>value[i];
		cout<<"max="<<knapsack()<<endl;
	}
	return 0;
}


参考 http://www.cppblog.com/jake1036/archive/2011/06/27/149566.html


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值