目录
1.在一个最佳优先分支界限算法中,我们应该使用什么样的数据结构来跟踪活节点?
2.对于本节求解的分配问题的相同实例,用基于矩阵列(而不是行)的边界函数以及最佳优先分支界限算法求解。
b.对于分配问题的分支界限算法来说,在最好的情况下,它的状态空间树会包含多少节点?
a.对于背包问题,给出一个比本节介绍的更复杂(也更好)的边界函数。
a.在旅行商问题的实例中,如果用整数对称矩阵表示城市间的距离,请证明公式(12.2)给出的下界的合法性。
b.对于非对称距离矩阵,我们应该如何修改下界公式(12.2)?
1.在一个最佳优先分支界限算法中,我们应该使用什么样的数据结构来跟踪活节点?
堆和最小堆分别用于最大化和最小化问题。
2.对于本节求解的分配问题的相同实例,用基于矩阵列(而不是行)的边界函数以及最佳优先分支界限算法求解。




3.
a.对于分配问题的分支界限算法,给出一个最优输入的例子。
矩阵的每个值都相同的时候
b.对于分配问题的分支界限算法来说,在最好的情况下,它的状态空间树会包含多少节点?
最好的时候,每一层都剪枝,只留一条路,那么第一层n个,第二层n-1个,最后一层1个
所以最好情况节点数 = n (n+1)/2
5.用分支界限算法对背包问题的以下实例求解。

计算分别的单个价值为:10、9、7、3
因此有初始根节点是16*10=160,确定下界,确定取1的情况下,W=6,此时有6*9+100=154,此时选2超重,再选3也超重,最后选4界为100+6*3=118,W=2。
而不选1的情况下,选2的界为16*9=144,此时选2,W=9,v=63,选3时界63+63=126,此时选3,W=1,v=63+56=119,选4则超重;若不选3,选4界为63+27=90,此时选4则W=5,v=75
不选2时,选3的界为112,此时选3,W=8,v=56,选4时界为56+32=88,此时选4为W=4,56+12=68
最终发现最近方案为2,3,v=119

6.
a.对于背包问题,给出一个比本节介绍的更复杂(也更好)的边界函数。
改进的上界函数:
- 按单位重量价值降序排序物品
- 尽可能装满整数物品
- 最后装一部分下一个物品填满背包
- 得到最紧的上界(比原来大很多,剪枝更强)
b.根据这个边界函数,用分支界限算法对第5题的实例求解。
排序后:
1: w=10, v=100, 单价 10
2: w=7, v=63, 单价 9
3: w=8, v=56, 单价 7
4: w=4, v=12, 单价 3
容量 W=16
- 根节点(没选任何物品)
- 当前重量 = 0,当前价值 = 0
- 剩余容量 = 16
计算最优上界:
- 装物品 1(w=10)→ 剩余 6
- 装物品 2(w=7)装不下,装物品 3(w=8)装不下
- 装物品 4 一部分:6×3=18
bound = 100 + 18 = 118
- 左分支:选物品 1
- 重量 = 10,价值 = 100
- 剩余 = 6
计算上界:
- 物品 2(w=7)装不下
- 物品 3(w=8)装不下
- 物品 4 可全装(w=4)→ 价值 + 12
- 剩余 2,无物品可装
bound = 100 + 12 = 112
这是可行解 :选 1+4,价值 112,重量 14
- 右分支:不选物品 1
- 重量 = 0,价值 = 0
- 剩余 = 16
计算上界:
- 装物品 2(w=7)→ 剩余 9
- 装物品 3(w=8)→ 剩余 1
- 装物品 4 一部分:1×3=3
bound = 63+56+3 = 122(虽然上界高,但实际最大只能到 63+56=119 <112?不:119>112,继续)
继续搜索后,最大可行解:物品 2+3=119 (重量 15)物品 2+4=75 物品 3+4=68
最大为 119
7.写一个程序用分支界限算法对背包问题求解。
#include <stdio.h>
#include <stdlib.h>
// 物品结构体
typedef struct {
int w; // 重量
int v; // 价值
double cp; // 单位价值
} Item;
// 队列节点(分支界限用)
typedef struct Node {
int level; // 当前层数(第几个物品)
int weight; // 当前总重量
int value; // 当前总价值
double bound; // 上界
} Node;
Item items[100];
int n, W; // 物品数、背包容量
int maxValue = 0; // 最优价值
// 按单位价值降序排序
int cmp(const void *a, const void *b) {
Item *i1 = (Item *)a;
Item *i2 = (Item *)b;
if (i1->cp < i2->cp) return 1;
return -1;
}
// 计算上界(第6题的最优边界:先装完整物品,再装分数)
double bound(Node u) {
if (u.weight >= W) return 0;
double res = u.value;
int totW = u.weight;
int i = u.level;
// 先装能装下的完整物品
while (i < n && totW + items[i].w <= W) {
totW += items[i].w;
res += items[i].v;
i++;
}
// 再装分数物品
if (i < n) {
res += items[i].cp * (W - totW);
}
return res;
}
// 分支界限核心算法
void knapsack() {
Node q[1000]; // 队列
int front = 0, rear = 0;
// 根节点
Node root;
root.level = 0;
root.weight = 0;
root.value = 0;
root.bound = bound(root);
q[rear++] = root;
while (front < rear) {
Node cur = q[front++];
// 更新最优解
if (cur.value > maxValue)
maxValue = cur.value;
// 到达最后一层,不扩展
if (cur.level == n) continue;
// 上界 <= 当前最优,剪枝
if (cur.bound <= maxValue) continue;
// ====================== 左孩子:选当前物品 ======================
Node left;
left.level = cur.level + 1;
left.weight = cur.weight + items[cur.level].w;
left.value = cur.value + items[cur.level].v;
if (left.weight <= W && left.value > maxValue) {
left.bound = bound(left);
q[rear++] = left;
}
// ====================== 右孩子:不选当前物品 ====================
Node right;
right.level = cur.level + 1;
right.weight = cur.weight;
right.value = cur.value;
right.bound = bound(right);
if (right.bound > maxValue) {
q[rear++] = right;
}
}
}
int main() {
// 第5题数据
n = 4;
W = 16;
items[0] = (Item){10, 100, 100 / 10.0};
items[1] = (Item){7, 63, 63 / 7.0};
items[2] = (Item){8, 56, 56 / 8.0};
items[3] = (Item){4, 12, 12 / 4.0};
// 按单位价值排序
qsort(items, n, sizeof(Item), cmp);
maxValue = 0;
knapsack();
printf("背包容量 W = %d\n", W);
printf("最优总价值 = %d\n", maxValue);
return 0;
}
8.
a.在旅行商问题的实例中,如果用整数对称矩阵表示城市间的距离,请证明公式(12.2)给出的下界的合法性。
- 任何 TSP 回路,每个城市恰好使用两条边。
- 对每个城市,回路中它的两条边之和≥ 该城市最小两条边之和。
- 对所有城市求和:∑i=1n(回路中i的两条边)≥s
- 左边每条边在回路中被两个端点各算一次,所以2L≥s
- 因此:L≥2s
- 因为 L 是整数,所以最小可能的整数上界就是L≥⌈2s⌉
b.对于非对称距离矩阵,我们应该如何修改下界公式(12.2)?
- 每条有向边只被计算一次,不会在两个方向重复计入 s
所以不再除以 2,直接用:lb=s
9.对于下面的图,应用分支界限算法求解旅行商问题。


先走ab

然后走ac,发现b没在c前,舍弃


再在ab的基础上选:cda或者dca
得出结果:

得出路径为abdca,l=11