算法设计与分析-习题12.2

目录

1.在一个最佳优先分支界限算法中,我们应该使用什么样的数据结构来跟踪活节点?

2.对于本节求解的分配问题的相同实例,用基于矩阵列(而不是行)的边界函数以及最佳优先分支界限算法求解。

3.

a.对于分配问题的分支界限算法,给出一个最优输入的例子。

b.对于分配问题的分支界限算法来说,在最好的情况下,它的状态空间树会包含多少节点?

5.用分支界限算法对背包问题的以下实例求解。

6.

a.对于背包问题,给出一个比本节介绍的更复杂(也更好)的边界函数。

b.根据这个边界函数,用分支界限算法对第5题的实例求解。

7.写一个程序用分支界限算法对背包问题求解。

8.

a.在旅行商问题的实例中,如果用整数对称矩阵表示城市间的距离,请证明公式(12.2)给出的下界的合法性。

b.对于非对称距离矩阵,我们应该如何修改下界公式(12.2)?

9.对于下面的图,应用分支界限算法求解旅行商问题。


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.对于背包问题,给出一个比本节介绍的更复杂(也更好)的边界函数。

改进的上界函数:

  1. 单位重量价值降序排序物品
  2. 尽可能装满整数物品
  3. 最后装一部分下一个物品填满背包
  4. 得到最紧的上界(比原来大很多,剪枝更强)

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
  1. 根节点(没选任何物品)
  • 当前重量 = 0,当前价值 = 0
  • 剩余容量 = 16

计算最优上界:

  • 装物品 1(w=10)→ 剩余 6
  • 装物品 2(w=7)装不下,装物品 3(w=8)装不下
  • 装物品 4 一部分:6×3=18

bound = 100 + 18 = 118


  1. 左分支:选物品 1
  • 重量 = 10,价值 = 100
  • 剩余 = 6

计算上界:

  • 物品 2(w=7)装不下
  • 物品 3(w=8)装不下
  • 物品 4 可全装(w=4)→ 价值 + 12
  • 剩余 2,无物品可装

bound = 100 + 12 = 112

这是可行解选 1+4,价值 112,重量 14


  1. 右分支:不选物品 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

相关推荐
Yzzz-F1 小时前
Problem - 2194E - Codeforces
算法
x_xbx1 小时前
LeetCode:83. 删除排序链表中的重复元素
算法·leetcode·链表
_小草鱼_2 小时前
【搜索与图论】DFS算法(深度优先搜索)
算法·深度优先·图论·回溯·递归
I_LPL2 小时前
hot100 栈专题
算法·
2401_879503412 小时前
C++中的观察者模式变体
开发语言·c++·算法
阿贵---3 小时前
C++中的备忘录模式
开发语言·c++·算法
setmoon2143 小时前
C++中的观察者模式实战
开发语言·c++·算法
2403_835568473 小时前
C++代码规范化工具
开发语言·c++·算法
tankeven3 小时前
HJ138 在树上游玩
c++·算法