实验目的:
通过本次实验,了解分支限界法的基本思想,掌握分支限界法在实际问题中使用方法。
实验环境:
硬件:PC机
软件:windows操作系统,C语言
实验内容:
求解0/1背包问题:通过队列式分枝限界法求解该问题。
实验学时:2
实验过程:
1.算法设计
这段程序实现了使用分枝界限法求解0/1背包问题(0/1 Knapsack Problem)。以下是该程序的算法设计:
1. 定义了结构体NodeType,用于表示节点的信息,包括节点编号no、当前处理的物品序号 i、当前背包重量w、当前背包总价值v、每个物品是否装入背包的状态x[]、节点的上界估计值 ub。
2. 实现了函数bound(NodeType &e),用于计算节点e的上界估计值 ub。该函数在当前节点的基础上,向后遍历未处理的物品,计算能够装入背包的最大总价值。
3. 实现了函数EnQueue(NodeType e, queue<NodeType> &qu),用于将节点e 入队列。如果当前节点处理到最后一个物品,且得到的总价值比之前的最大价值大,则更新最大价值和最优解。
4. 实现了函数bfs(),用于进行分枝界限法求解。该函数初始化第一个节点,将其入队列,然后循环处理队列中的节点,根据当前节点状态进行分支限界,生成新的节点,并将满足条件的节点入队列。
5. 实现了main()函数,在该函数中调用bfs()函数进行求解,并输出结果。
总体思路是通过不断生成新的节点,根据上界估计值进行剪枝,从而逐步搜索到最优解。
2.程序清单
#include<stdio.h>
#include<queue>
using namespace std;
#define MAXN 20
//物品数量,背包承重最大值
int n = 3, W = 30;
//重量
int w[] = { 0,16,15,15 };
//价值
int v[] = { 0,45,25,25 };
//最大价值
int maxv = -9999;
//最优解
int bestx[MAXN];
//解的节点数量
int total = 1;
//队列中的节点
struct NodeType
{
int no;
int i;
int w;
int v;
int x[MAXN];
double ub;
};
void bound(NodeType &e)
{
int i = e.i + 1;
int sumw = e.w;
double sumv = e.v;
while ((sumw + w[i] <= W) && i <= n)
{
sumw += w[i];
sumv += v[i];
i++;
}
if (i <= n)
e.ub = sumv + (W - sumw) * v[i] / w[i];
else
e.ub = sumv;
}
//节点入队列
void EnQueue(NodeType e, queue<NodeType> &qu)
{
if (e.i == n)
{
if (e.i > maxv)
{
maxv = e.v;
for (int j = 1; j <= n; j++)
bestx[j] = e.x[j];
}
}
else qu.push(e);
}
//求最优解
void bfs()
{
int j;
NodeType e, el, e2;
queue<NodeType> qu;
e.i = 0;
e.w = 0;
e.v = 0;
e.no = total++;
for (j = 1; j <= n; j++)
e.x[j] = 0;
bound(e);
qu.push(e);
while (!qu.empty())
{
e = qu.front();
qu.pop();
if (e.w + w[e.i + 1] <= W)
{
el.no = total++;
el.i = e.i + 1;
el.w = e.w + w[el.i];
el.v = e.v + v[el.i];
for (j = 1; j <= n; j++)
el.x[j] = e.x[j];
el.x[el.i]=1;
bound(el);
EnQueue(el, qu);
}
e2.no = total++;
e2.i = e.i + 1;
e2.w = e.w;
e2.v = e.v;
for (j = 1; j <= n; j++)
e2.x[j] = e.x[j];
e2.x[e2.i] = 0;
bound(e2);
if (e2.ub > maxv)
EnQueue(e2,qu);
}
}
void main()
{
bfs();
printf("分枝界限法求解0/1背包问题:\nX=[");
for (int i = 1; i <= n; i++)
printf("],装入总价值为%d\n", maxv);
}
3.复杂度分析
(1)时间复杂度
该程序使用了分枝界限法来解决0/1背包问题。在最坏情况下,需要考虑所有可能的解空间,因此时间复杂度是指数级别的,即 O(2^n),其中 n 是物品的数量。
- 空间复杂度
空间复杂度主要取决于存储节点信息的数据结构和队列的空间占用。在这段程序中,使用了结构体NodeType存储节点信息,并且使用了 STL 中的队列queue<NodeType>存储节点队列。此外,还有一些额外的数组用于存储物品的重量、价值等信息。因此,空间复杂度主要由这些数据结构和数组的空间占用决定,是 O(n) 级别的,其中n是物品的数量。
4.运行结果
实验总结:
收获与体会:
我了解了分枝界限法在解决组合优化问题中的应用,特别是在解决0/1背包问题时的实现方式。熟悉了如何利用队列来管理节点,通过不断扩展节点的方式搜索最优解。理解了如何通过界限函数(bound function)来剪枝,减少搜索空间,提高算法效率。通过实验结果,可以看到算法得出的最优解和装入总价值,加深了对算法运行过程的理解。
今后的努力方向:
我将深入理解分枝界限法的原理和优化技巧,掌握更多的剪枝策略,以提高算法的效率和性能。学习其他高级的组合优化算法,如动态规划、贪心算法、遗传算法等,扩展解决问题的思路和方法。