前段时间因为忙着做老师的项目,一直没有太多时间去刷题和研究算法,最近稍微空闲了一些。因为发现自己在动态规划问题上的薄弱,加上背包问题是典型的利用动态规划求最优解的问题,因此决定将网上流传的“背包九讲”学习一遍。今天阅读了最简单的一讲: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;
}
此处采用的是没有优化过空间复杂度的算法。优化空间复杂度的情形下,只需要设计一个一维动态数组,且在二维的遍历方向是从高到低。