回溯法:0/1背包问题(利用价值密度进行优化)
// 16x5.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
// 回溯法:0/1背包问题(利用价值密度进行优化)
// 左孩子的界限函数的价值与父节点相同,因为总价值=cp+子树价值。当向左孩子移的时候,那部分价值已经计入了新的cp了。
#include <iostream.h>
#include "msort.h"
#include "object.h" // 内含两部分数据:物品标识,物品价值密度
template<class Tw, class Tp>
class Knap {
friend Tp Knapsack(Tp *, Tw *, Tw, int);
private:
Tp Bound(int i);
void Compute(int i);
Tw c; // 背包容量
int n; // 物品的数量
Tw *w; // 物品重量的数组
Tp *p; // 物品价值的数组
Tw cw; // 当前背包的重量
Tp cp; // 当前背包的价值
Tp bestp; // 迄今最大的收益
};
// 返回子树最大价值上限(到达树叶才会有)
template<class Tw, class Tp>
Tp Knap<Tw, Tp>::Bound(int i)
{
// 重要:凡是进入这个函数的,就已经在(预测)计算从第i层开始的子树的价值了。
// 就是说,加上所有子树的价值(包括部分价值)
Tw cleft = c - cw; // 剩余容量
Tp b = cp; // 价值边界(当前背包的价值)
// 根据价值密度填充剩余容量
while (i <= n && w[i] <= cleft) {
cleft -= w[i];
b += p[i];
i++;
}
// 使用下一个物品的部分价值
if (i <= n) b += p[i]/w[i] * cleft;
cout << " Bound=" << b << endl;
return b;
}
template<class Tw, class Tp>
void Knap<Tw, Tp>::Compute(int i)
{
if (i > n) { // 到达叶节点
bestp = cp;
return;
}
// 如果当前背包的重量cw + 当前物品重量w[i] <= 背包总容量c,就继续深度遍历
if (cw + w[i] <= c) {
cw += w[i];
cp += p[i];
Compute(i+1); //一路深度遍历的时候,不会碰到Bound函数
cw -= w[i];
cp -= p[i];
}
// 只有向左孩子移动时,界限函数才会被计算
if (Bound(i+1) > bestp) // 计算下一层的边界,是否大于迄今最大收益
Compute(i+1);
}
// 返回背包的最佳装填方案的总价值
// 这里的Tw和Tp都是int类型,而不是一个数组类型
template<class Tw, class Tp>
Tp Knapsack(Tp p[], Tw w[], Tw c, int n)
{
Tw W = 0; // 重量之和
Tp P = 0; // 价值之和
Object *Q = new Object [n]; // 生成物品数组,并根据其价值密度排序
for (int i = 1; i <= n; i++) {
Q[i-1].ID = i; // 物品的编号命名
Q[i-1].d = 1.0*p[i]/w[i]; // 计算物品价值密度
P += p[i];
W += w[i];
}
// 如果所有物品重量之和小于背包容量,全部填进去就行了,直接返回。
if (W <= c) return P;
// 根据物品价值密度排序,传进去物品数组和物品数量
// 但是Q的下标是从0开始的
MergeSort(Q,n);
for (i = 0; i < n; i++) {
cout<< Q[i].ID << " " << Q[i].d << endl;
}
Knap<Tw, Tp> K;
K.p = new Tp [n+1];
K.w = new Tw [n+1];
// 根据排序的结果给背包的物品赋值
// 但是此时物品的顺序已经变了
for (i = 1; i <= n; i++) {
K.p[i] = p[Q[i-1].ID];
K.w[i] = w[Q[i-1].ID];
}
K.cw = 0;
K.cp = 0;
K.c = c;
K.n = n;
K.bestp = 0;
// 开始计算最佳装载
K.Compute(1);
delete [] Q;
delete [] K.w; // 这些东西应该放到析构函数里去
delete [] K.p;
return K.bestp;
}
void main(void)
{
int p[6] = {0, 6, 3, 5, 4, 6}; // 背包物品的价值
int w[6] = {0, 2, 2, 6, 5, 4}; // 背包物品的重量
int n = 5; // 背包物品的数量
int c = 10; // 背包的总容量
cout << "Optimal value is" << endl;
cout << Knapsack(p,w,c,n) << endl;
}
// ----------------------Object.h文件------------------------------------------
#ifndef Object_
#define Object_
class Object {
friend int Knapsack(int *, int *, int, int);
public:
int operator<=(Object a) const { return (d >= a.d); }
private:
int ID; // 物品的表示
float d; // 物品价值的密度
};
#endif
// ----------------------msort.h文件------------------------------------------
// file msort.h
// merge sort
#ifndef MergeSort_
#define MergeSort_
template<class T>
void Merge(T c[], T d[], int l, int m, int r)
{// Merge c[l:m]] and c[m:r] to d[l:r].
int i = l, // cursor for first segment
j = m+1, // cursor for second
k = l; // cursor for result
// merge until i or j exits its segment
while ((i <= m) && (j <= r))
if (c[i] <= c[j]) d[k++] = c[i++];
else d[k++] = c[j++];
// take care of left overs
if (i > m) for (int q = j; q <= r; q++)
d[k++] = c[q];
else for (int q = i; q <= m; q++)
d[k++] = c[q];
}
template<class T>
void MergePass(T x[], T y[], int s, int n)
{// Merge adjacent segments of size s.
int i = 0;
while (i <= n - 2 * s) {
// merge two adjacent segments of size s
Merge(x, y, i, i+s-1, i+2*s-1);
i = i + 2 * s;
}
// fewer than 2s elements remain
if (i + s < n) Merge(x, y, i, i+s-1, n-1);
else for (int j = i; j <= n-1; j++)
// copy last segment to y
y[j] = x[j];
}
template<class T>
void MergeSort(T a[], int n)
{// Sort a[0:n-1] using merge sort.
T *b = new T [n];
int s = 1; // segment size
while (s < n) {
MergePass(a, b, s, n); // merge from a to b
s += s;
MergePass(b, a, s, n); // merge from b to a
s += s;
}
}
#endif