实验描述
0-1背包问题的回溯法求解过程分析
设有n个物品,每个物品的重量为wi价值为pi,背包的容量为c,希望将其中的部分物品装入背包中,在不超过背包容量的情况下,希望物品的总价值最大。
输入:
输入第一行为物品数量n以及背包容量c,第2行到第n+1行,每行有两个整数wi, pi,分别表示物品的重量以及价值。
实验题目
实验7.1源码
#include <iostream>
using namespace std;
template <class Typep, class Typew>
Typep Knapsack(Typep[], Typew[], Typew, int); //负责对变量初始化,调用递归函数 Backtrack(1)实现回溯搜索并返回最大装包价值
template <class Typew, class Typep>
class Knap // Knap类记录解空间树的结点信息
{
friend Typep Knapsack(Typep[], Typew[], Typew, int);
public:
Typep Bound(int i); //计算以当前结点为根的子树的价值上界
void Backtrack(int i); //核心函数,对解空间树回溯搜索,求得最大装包价值
Typew c; //背包容量
int n; //物品数
Typew *w; //物体重量数组
Typep *p; //物体价值数组
Typew cw; //当前重量
Typep cp; //当前价值
Typep bestp; //当前最优价值
};
template <class Typep, class Typew>
class Object //定义对象类,作用相当于结构体
{
friend Typep Knapsack(Typep[], Typew[], Typew, int);
public:
int operator<(Object a) const //符号重载函数,重载<符号
{
return d >= a.d;
}
int ID; //编号
float d; //单位重量的价值
};
template <class Typep, class Typew>
void Sort(class Object<Typep, Typew> *Q, int n); //按单位价值从大到小排序
int main() // Knapsack(p, w, c, n)
{
int *p, *w, c, n, bestp;
cin >> n >> c;
p = new int[n + 1];
w = new int[n + 1];
for (int i = 1; i <= n; i++)
{
cin >> w[i] >> p[i];
}
bestp = Knapsack(p, w, c, n);
cout << bestp << endl;
return 0;
}
template <class Typew, class Typep>
void Knap<Typew, Typep>::Backtrack(int i)
{
//如果搜索到了最后一个物品。则求出最优价值。
if (i > n)
{
bestp = cp;
return;
}
//如果已装物品重量+当前待装物品重量小于背包容量
//则将其装入
if (cw + w[i] <= c)
{
cw += w[i];
cp += p[i];
Backtrack(i + 1);
cw -= w[i];
cp -= p[i];
//如果以i+1为根的子树上所有物品最大值比当前找到的最大价值要打
//则有必要搜
}
}
//计算当前结点处的上限,此处采用的是贪心算法
//可以证明通过算法获得的值在一般情况下是不能达到的
template <class Typew, class Typep>
Typep Knap<Typew, Typep>::Bound(int i)
{
Typew cleft = c - cw; //背包所能承受的重量
Typep b = cp; //当前价值
//在物品数量小于总物品数量以及物品重量小于背包的剩余重量时,
//将该物品装入背包中,并增加相应价值
while (i <= n && w[i] <= cleft)
{
cleft -= -w[i];
b += p[i];
i++;
}
//如果所装入物品数量小于整个物品的个数,则可装入第i个物品的部分,
//从而达到背包能装入物品的最大价值
if (i <= n)
b += p[i] * cleft / w[i];
return b;
}
template <class Typep, class Typew>
Typep Knapsack(Typep p[], Typew w[], Typew c, int n)
{
//为Knap::Backtrack初始化
Typew W = 0;
Typep P = 0;
Object<Typep, Typew> *Q = new Object<Typep, Typew>[n + 1]; //创建Object类的对象数组|
//计算单位重量的价值,并计算总的价值和总的重量
for (int i = 1; i <= n; i++) //初始化Object类的对象数组|
{
Q[i].ID = i;
Q[i].d = 1.0 * p[i] / w[i];
P += p[i]; //总的价值
W += w[i]; //总的重量
}
//如果总的重量小于背包所承受的重量,则返回总的价值
if (W <= c)
return P;
//按照单位价值的大小从大到小排序
Sort(Q, n); //不是库函数,是自己定义的,要按照单位重量去排序
//按照单位价值的大小从大到小的排序,并将其价值和重量并保存到p和w中
Knap<Typew, Typep> K;
K.p = new Typep[n + 1];
K.w = new Typew[n + 1];
for (int i = 1; i <= n; i++)
{
K.p[i] = p[Q[i].ID];
K.w[i] = w[Q[i].ID];
}
K.cp = 0;
K.cw = 0;
K.c = c;
K.n = n;
K.bestp = 0;
//搜索最优值
K.Backtrack(1);
delete[] Q;
delete[] K.w;
delete[] K.p;
return K.bestp;
}
template <class Typep, class Typew>
void Sort(class Object<Typep, Typew> *Q, int n)
{
int k;
//直接插入排序算法,按到从大到小的顺序排序
for (int i = 2; i <= n; i++)
{
if (Q[i].d < Q[i - 1].d)
{
Q[0] = Q[i]; // Q[0]=temp作用
for (int k = i - 1; !(Q[k].d < Q[0].d); k--)
{
Q[k + 1] = Q[0];
}
}
return;
}
}
要求
算法分析
//后续补充
算法测试
// 6 80 40 50 60 70 30 40 20 30 50 60 10 20
测试结果
110