目录
1.问题描述
给定n个物品和一个容量为C的背包,物品的重量为w,价值为v,背包问题(knapsack problem)是如何选择装入背包的物品,使得装入背包中物品的总价值最大。需要注意的是此背包问题能够使用贪心算法的一部分原因是能够将该物体部分装入,所以又称为部分背包问题。
0/1背包问题(0/1 knapsack problem):求这些物品中一个最有价值的子集,其中的物品是不可分的。0/1背包问题需要使用动态规划的方法。
部分背包问题:求这些物品中一个最有价值的子集,可以将物品í的一部分装入背包中。
2.问题分析
贪心策略:可以考虑三种策略
(1)按照价值最大进行策略抉择
选择价值最大的物品,因为这可以尽可能快地增加背包的总价值。但背包容量却可能消耗得太快使得装入背包的物品个数减少,从而不能保证目标函数达到最大。
(2)按照重量最轻进行策略抉择
选择重量最轻的物品,因为这可以装入尽可能多的物品,从而增加背包的总价值。然而背包的价值却没能保证迅速增长,从而不能保证目标函数达到最大。
(3)按照单位重量价值最大进行策略抉择
选择单位重量价值最大的物品,在背包价值增长和背包容量消耗两者之间寻找平衡。
故只有按照单位重量价值最大进行策略抉择才能最好的解决问题
'单位重量价值最大”策略
每次从物品集合中选择单位重量价值最大的物品如果其重量小于背包容量,就可以把它装入,并将背包容量减去该物品的重量。
3.算法分析
1.首先为了实现按照单位重量价值最大的策略进行贪心选择,我们需要对存放重量以及价值的数组进行重新排序,排序的规则就是单位重量价值的降序排序。故需要写一个排序算法,将这俩个数组重新排序。
2.将存放问题的解的数组x初始化为0。
3.定义变量i并初始化为0。
4.通过while循环结构,控制循环,直到w[i]>c,即当前物品的重量大于剩余的容量,则直接退出循环。
5.在while循环中,将第i个物品全部放入背包,即x[i]赋值为1,然后更新当前容量剩余值。接着让i++,重新进入循环。
6.通过if语句判断,循环是否提前结束,若提前结束则代表此时第i个物品只能部分装入背包,令x[i]=c/w[i]。
4.算法实现
void Knapsack(int n, float C, float v[], float w[], float x[])
{
sort(n, v, w); //将重量数组与价值数组按照单位重量价值降次排序
int i=0;
for (i = 0; i < n; i++)
{
x[i] = 0; //将问题的解数组初始化
}
float c = C; //c表示当前剩余容量大小
for (int i = 0; i < n; i++)
{
if (w[i] > c)
{
//如果当前第i个物品重量大于剩余容量,则第i个物品无法全部装入,退出循环
break;
}
x[i] = 1; //第i个物品全部装入
c -= w[i];
}
if (i < n)//遇到break提前退出
{
x[i] = c / w[i]; //将第i个物品部分装入
}
}
5.完整代码示例
#include<iostream>
using namespace std;
void sort(int n, float* v, float* w)
{
float* m = new float[n]; //定义一个新的数组m用于存储单位重量价值
for (int i = 0; i < n; i++)
{
m[i] = v[i] / w[i];//用m[]存商品的单位重量价值
}
for (int i = 0; i < n; i++) { //按照贪心策略每次把单位重量商品的价值最大的商品放在前面
for (int j = i + 1; j < n; j++) {
if (m[i] < m[j]) {
float temp = m[i];
m[i] = m[j];
m[j] = temp;
float temp1 = w[i];//把商品的重量和价值同时放到最前面
w[i] = w[j];
w[j] = temp1;
float temp2 = v[i];
v[i] = v[j];
v[j] = temp2;
}
}
}
//将堆栈数组m主动释放
delete []m;
}
void Knapsack(int n, float C, float v[], float w[], float x[])
{
sort(n, v, w); //将重量数组与价值数组按照单位重量价值降次排序
int i=0;
for (i = 0; i < n; i++)
{
x[i] = 0; //将问题的解数组初始化
}
float c = C; //c表示当前剩余容量大小
for (int i = 0; i < n; i++)
{
if (w[i] > c)
{
//如果当前第i个物品重量大于剩余容量,则第i个物品无法全部装入,退出循环
break;
}
x[i] = 1; //第i个物品全部装入
c -= w[i];
}
if (i < n)//遇到break提前退出
{
x[i] = c / w[i]; //将第i个物品部分装入
}
}
int main()
{
//以三个物品进行举例
float C = 50;//背包所能容纳的重量
float w[] = {20,30,10 };
float v[] = {60,120,50 };
float x[3];
cout << "背包所能容纳的重量为:" << C << endl;
cout << "待装物品的重量和价值分别为:" << endl;
for (int i = 0; i <3; i++)
{
cout << "第" << i << "个物品: (" << w[i] << "," << v[i] << ")" << endl;
}
Knapsack(3, C, v, w, x);
cout << "选择装下的物品比如下:" << endl;
for (int i =0; i < 3; i++)
{
cout << "第" << i << "个物品: " << x[i] << endl;
}
return 0;
}