前两天忙着跑OPNET上的程序,没太多时间看背包九讲。下午的时候抽空看了一下完全背包问题,利用晚上的时间将完全背包问题的两种解决方法用C++写了一下。不过由于缺少测试用例,不保证正确性。
#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
//完全背包问题,包括两种方案,此处采用将一件物品拆成若干价格为2^k*Ci,价值为2^k*Vi(1<=2^k<=V/Ci)的物品,从而转化成01背包问题的方法
struct goods
{
int cost,value; //价格与价值
int index; //标识其属于哪个原始物品
goods(int c, int v, int i):cost(c),value(v),index(i){} //默认构造方法
};
int _tmain(int argc, _TCHAR* argv[])
{
int volumn,number; //输入背包容量与物品数目
cout<<"Please input the volumn of the package and the number of goods:"<<endl;
cin>>volumn>>number;
cout<<"Please input the data of goods(sorted in index):"<<endl;
int temp=1,total=1;
vector<goods> data; //存放所有物品的数组
vector<int> corres(number+1,0); //物品编号与单价对应表
goods ini(0,0,0);
data.push_back(ini); //放在第0个位置
while(temp<=number)
{
int a,b;
cin>>a>>b; //输入各个物品的价格与价值
int n=volumn/a; //最多能放的物品数
int k=1,count=0;
corres[temp]=a;
while(k<=n)
{
goods g(k*a,k*b,temp);
k*=2;
count++;
data.push_back(g);
}
temp++;
total+=count;
}
vector< vector<int> > f(total, vector<int>(volumn+1,0)); //二维递归数组,第一维表示所用物品个数,第二维表示包的容量,初始化均为0
for(int i=1; i<total; i++)
{
for(int p=1; p<data[i].cost; p++)
f[i][p]=f[i-1][p];
for(int j=data[i].cost; j<=volumn; j++)
f[i][j]=max( f[i-1][j],f[i-1][j-data[i].cost]+data[i].value );
}
cout<<"The maximum volumn of the package is:"<<f[total-1][volumn]<<endl; //输出最后结果
cout<<"The index of picked goods(start from 1) and their number is:"<<endl;
vector<int> res(number+1,0);
int tmp=volumn,q=total-1;
while(q>0) //从后向前检查所选物品
{
if(f[q][tmp]!=f[q-1][tmp]) //说明中选
{
int r=data[q].index; //编号
res[r]+=data[q].cost/corres[r];
tmp-=data[q].cost;
}
q--;
}
for(int i=1;i<=number;i++)
if(res[i]!=0)
cout<<i<<" "<<res[i]<<endl;
return 0;
}
第一种方法是在0-1背包问题的基础上修正的。虽然每种物品的数量表面上不做限制,但由于背包容量的存在,其实是存在上限的。将这个上限通过二进制表达出来,进一步可以转化为0-1背包问题。举个例子,比如某种物品塞入背包的上限是6(它的价格与价值分别为1和2),则无论最后塞入背包的数目是{0,1,2,3,4,5,6}中的哪一个,都可以用2^0=1,2^1=2,2^2=4这三个数组合表示。进一步地,可将“塞入多个这样物品”的问题转化为物品类型为(cost=1,value=2),(cost=2,value=4),(cost=4,value=8)的0-1背包问题。背包九讲中还提出剪枝的方法,比如先筛选掉价格相对高且价值相对低的物品(因为其竞争力必然较低),此处没有实现。
#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[]) //完全背包问题的第二种解法,算法复杂度为O(nv)
{
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);
}
/**
设计一维数组,用于动态规划
含义是:在背包容量为volumn时可获得的最大价值
**/
vector<int> f(volumn+1,0);
for(int j=1;j<=number;j++)
{
for(int k=data[j].cost;k<=volumn;k++)
{
f[k]=max(f[k],f[k-data[j].cost]+data[j].value);
}
}
cout<<"The maximum value we can get is:"<<f[volumn]<<endl;
return 0;
}
第二种方法较第一种无论是在算法复杂度还是空间复杂度上都降低了很多。它基于“单个物品可以取无穷次”的便利性,将0-1背包问题的解法中二重循环的方向改为从小到大递增,如上所示。