- 实验内容
-
0-1背包问题
给定n种物品和一个背包。物品i的重量是w[i],其价值为vi,背包的容量为C。问如何选择装入背包的物品,使得装入背包中物品的总价值最大?
在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品装入背包多次,也不能只装入部分的物品,因此称之为0-1背包问题。
2. 小数背包问题
给定n种物品和一个背包。物品i的重量是w[i],其价值为vi,背包的容量为C。问如何选择装入背包的物品,使得装入背包中物品的总价值最大?
在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包。
1.0-1背包:
#include <iostream>
#include <string>
using namespace std;
const int N = 150;
int traceback(int n, int c, int w[N], int v[N]);
int max(int a, int b);
int main()
{
int v[N]; //价值数组
int w[N]; //重量数组
int n, c; //n个数,c容量
cout << "请输入物品的个数: ";
cin >> n;
cout << "请输入背包的容量: ";
cin >> c;
int i, j;
for (i = 1; i <= n; i++)
{
cout << "请输入第" << i << "个物品的质量: ";
cin >> w[i];
}
for (j = 1; j <= n; j++)
{
cout << "输入第" << j << "个物品的价值: ";
cin >> v[j];
}
cout << "最大价值:" << traceback(n, c, w, v) << endl;
return 0;
}
int traceback(int n, int c, int w[N], int v[N])
{
int V[N][N];
int i, j;
for (i = 0; i <= n; i++)
V[i][0] = 0;
for (j = 0; j <= c; j++)
V[0][j] = 0;
//计算第i行,进行第i次迭代
for (i = 1; i <= n; i++)
{
for (j = 1; j <= c; j++)
{
if (j < w[i])
V[i][j] = V[i - 1][j];
else
V[i][j] = max(V[i - 1][j], V[i - 1][j - w[i]] + v[i]);
}
}
//装入物品
int x[N];
j = c;
cout << "装入的物品:";
for (i = n; i > 0; i--)
{
if (V[i][j] > V[i - 1][j])
{
x[i] = 1;
j = j - w[i];
}
else
x[i] = 0;
}
for (i = 1; i <= n; i++)
cout << x[i] << '\t';
cout << endl;
return V[n][c];
}
int max(int a, int b)
{
if (a >= b)
return a;
else
return b;
}
2.小数背包
#include <iostream>
#include <string>
using namespace std;
const int N = 150;
void traceback(int n, int c, int w[N], int v[N]);
int max(float p[N], int n);
int main()
{
int v[N]; //价值数组
int w[N]; //重量数组
int n, c; //n个数,c容量
cout << "请输入物品的个数: ";
cin >> n;
cout << "请输入背包的容量: ";
cin >> c;
int i, j;
for (i = 0; i < n; i++)
{
cout << "请输入第" << i + 1 << "个物品的质量: ";
cin >> w[i];
}
for (j = 0; j < n; j++)
{
cout << "输入第" << j + 1 << "个物品的价值: ";
cin >> v[j];
}
traceback(n, c, w, v);
return 0;
}
void traceback(int n, int c, int w[N], int v[N]) {
float p[N];//价值权重
int i;
int weight = 0; float value = 0;//总重量,总价值
for (i = 0; i < n; i++) //权重计算
p[i] = v[i] / (w[i] * 1.0);
for (i = 0; i < n; i++) {
int num = max(p, n);//价值最高的下标
weight += w[num];
if (weight < c) {
cout << "装入物品" << i + 1 << "的重量为 " << w[num] << " 价值为 " << v[num] << " 完整装入" << endl;
value += v[num];
}
else if (weight > c) {
float w1 = (c - (weight - w[num])) / (w[num] * 1.0);//计算装入部分
cout << "装入物品" << i + 1 << "的重量为 " << w[num] << " 价值为 " << v[num] << " 装入百分之" << w1 * 100 << endl;
weight = c;
value += w1 * v[num];
}
else break;
}
cout << "装入的总重量为 " << weight << endl;
cout << "装入的总价值为 " << value << endl;
}
int max(float p[N], int n) {
float max = p[0];
int maxi = 0;
for (int i = 0; i < n; i++) {
if (p[i] > max) {
max = p[i];
maxi = i;
}
}
p[maxi] = 0;
return maxi;
}
程序测试及运行结果:
1.0-1背包
2.小数背包
分析与讨论:
动态规划方法的原理就是把多阶段决策过程转化为一系列的单阶段决策问题,利用各个阶段之间的递推关系,逐个确定每个阶段的最优化决策,最终堆叠出多阶段决策的最优化决策结果。。
0-1背包问题具有最优子结构性质,所以可以用动态规划方法求解。根据这种性质定义递归关系并建立递归方程,以自底向上的方式计算最优值。而且以后编程时要彻底理解问题后再构造算法。
贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
贪心算法是按照各种量度标准排序物品。对于每一种排序结果,在背包容量内,选择量度最大的物品,继续选择次量度最大的物品,直到选到当前量度最大物品的重量大于背包剩余容量时(容不下),放入物品的一部分,这部分和背包剩余容量相等。打印出各物品是否被选择,或者被选择的比例。
贪心选择性质:所求问题的全局最优解可以通过一系列局部最优的选择(即贪心选择)来达到。