背包九讲自学手册——第一讲

      前段时间因为忙着做老师的项目,一直没有太多时间去刷题和研究算法,最近稍微空闲了一些。因为发现自己在动态规划问题上的薄弱,加上背包问题是典型的利用动态规划求最优解的问题,因此决定将网上流传的“背包九讲”学习一遍。今天阅读了最简单的一讲:0-1背包问题,并在本地用C++对其进行了实现。

      背包问题的题干很简单:给定互不相同的若干商品各一件,以及它们各自的价格与价值(限制为整数)。在背包容量(也即可供使用的总钱数)一定的条件下,如何确定可塞入背包的商品,使其总价值最大?

       最朴素的想法就是一个个地去试。商品种类少的时候还好,一旦多起来运算量就变得十分恐怖(指数级复杂度)。背包九讲中首先给出一个递推公式:

                       

      此处,表示前i个包中容量为v时可得到的最大价值。经过线性规划后的复杂度降为O(nv),具体的推导过程不再阐释。Visual Studio平台下的实现代码如下所示:

#include "stdafx.h"
#include <iostream>
#include <vector>
#include <stack>

using namespace std;

struct goods{
	int cost;          //价格
	int value;         //价值
	goods(int p=0,int q=0):cost(p),value(q){}          //默认构造函数
};

int _tmain(int argc, _TCHAR* argv[])          //使用动态规划思想解决背包问题
{
	int number,volumn;                   //输入物品数与背包容量
	cout<<"Please input the number of goods and the volumn of the package:"<<endl;
	cin>>number>>volumn;
	cout<<"Please input the price and value of each goods(sorted in index):"<<endl;
	vector<goods> data; 
	goods ini;
	data.push_back(ini);                //输入第零个商品
	for(int i=0;i<number;i++)           //按序输入商品数据
	{
		int a,b;
		cin>>a>>b;
		goods temp(a,b);
		data.push_back(temp);
	}

    /** 
	设计二维数组,用于动态规划,一维是商品数,二维是背包容量,
	含义是:在商品数目为number,背包容量为volumn时可获得的最大价值 
	**/

	vector< vector<int> > f(number+1,vector<int>(volumn+1,0));                   
	for(int j=1;j<=number;j++)
	{
		for(int c=1;c<data[j].cost;c++)
			f[j][c]=f[j-1][c];
		for(int k=data[j].cost;k<=volumn;k++)
			f[j][k]=max(f[j-1][k],f[j-1][k-data[j].cost]+data[j].value);
	}
	cout<<"The maximum value we can get is:"<<f[number][volumn]<<endl;
	cout<<"The indexes of goods which is picked are listed below:"<<endl;
	stack<int> res;
	int l=number;
	int tp=volumn;
	while(l>0)                         //倒过来追踪商品
	{
		if(f[l][tp]!=f[l-1][tp])
		{
			res.push(l);
			tp-=data[l].cost;
		}
		l--;
	}
	while(!res.empty())
	{
		cout<<res.top()<<" ";
		res.pop();
	}
	cout<<endl;
	return 0;
}
       此处采用的是没有优化过空间复杂度的算法。优化空间复杂度的情形下,只需要设计一个一维动态数组,且在二维的遍历方向是从高到低。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值