最近复习了一下算法,觉得背包问题最具代表性。可以用大多数通用的算法(穷举,动规,回溯,分枝限界)来解决。因此将最近的笔记整理一下,发到自己的博客上。
问题描述:背包的容量为C,现有N件物品,价格分别为p[0],p[1]......p[n-1].重量分别为:w[0],w[1]......w[n-1].从N件物品中选择任意个放入背包中,使得物体的价值最大并且总重量不超过背包的容量C。
采用数学语言描述如下:
在 w[0]*x[0] + w[1] *x[1]+....... +w[n-1]*x[n-1] < C, x[i] = 0 或1 的条件下
求 p[0]*x[0] + p[1] *x[1]+....... +p[n-1]*x[n-1] 的最大值。
穷举算法的基本思想如下:
枚举也所有可能的情况,找出其中的最大值。
用N位二进制数来表示N件物品的选取情况,例如在N=3的情况下 ‘000’代表未选取任何物品,而 ‘101’代表选取了第1件和第3件物品。因此将 0 ---- 2^n-1 的情况枚举,找也最大值即可。
算法如下:
using namespace std;
class PackEnum
{
protected:
vector<int> m_p; //N个背包的价格
vector<int> m_w; //N个背包的重量
int m_c; //背包的容量
int m_num; //物品的件数
public:
//构造函数
PackEnum();
PackEnum(vector<int>& p,vector<int>& w, int c,int n)
:m_p(p),m_w(w),m_c(c),m_num(n)
{}
//获取背包内物品的最大值
int GetBestValue() const
{
int bestValue=0; //背包最大价值
int currentValue =0; //当前背包中物品的价值
int currentWeight =0; //当前背包中物品的重量
const unsigned int max = 2 << m_num; //2的N次方
//枚举所有可能的解
for(unsigned int i =0; i<max;++i)
{
//清空背包
currentValue =0;
currentWeight =0;
//取2进制数的每一位
unsigned int bit = i;
int j =0;
while(bit !=0)
{
currentWeight += m_w[j] * (bit & 1);
currentValue += m_p[j] * (bit & 1);
//如果bit的最低位为1,则bit&1 = 1 ,否则为0
bit >>=1;
++j;
}
//保存最优解
if(currentWeight <=m_c
&& bestValue < currentValue)
{
bestValue = currentValue;
}
}
return bestValue;
}
};
int main(void)
{
//测试程序
int n;
int c;
cout << "请输入物品的件数" << endl;
cin >>n;
cout << "请输入背包的容量" << endl;
cin >>c;
vector<int> w(n);
vector<int> p(n);
cout << "请输入物品的重量:" << endl;
for(int i=0;i<n;++i)
cin >> w[i];
cout << "请输入物品的价格:" << endl;
for(int j=0;j<n;++j)
cin >> p[j];
PackEnum pack(p,w,c,n);
int bestValue = pack.GetBestValue();
cout << "背包内的物品的最大价值为:" << bestValue << endl;
return 0;
}
分析:
些算法的时间复杂度为 2^n, 并且要求(n<=32),所以算法的效率不高。但枚举算法是一种比较通用的算法,通常在找不到最佳的算法时,采用些算法。