算法-分支限界算法

1.算法思想

分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。
此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所需的解或活结点表为空时为止。

1.1 求解目标

分支限界法的求解目标是:找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。

1.2 两个重要机制

  • 产生分支(解空间树)
  • 产生一个界限,能终止许多分支(剪枝)

1.3 分支限界法与回溯法的区别

下表列出了回溯法和分支限界法的一些区别
在这里插入图片描述

1.4 两种分支限界法

从活结点表中选择下一扩展结点的不同方式导致不同的分支限界法。

最常见的有以下两种方式:

  • 队列式(FIFO)分支限界法:队列式分支限界法将活结点表组织成一个队列,并按队列的先进先出原则选取下一个结点为当前扩展结点。
  • 优先队列式分支限界法:优先队列式分支限界法将活结点表组织成一个优先队列,按优先队列中规定的结点优先级选取优先级最高的下一个结点成为当前扩展结点。
常用堆来实现优先队列

2.单向源最短路径

2.1 单向源最短路径问题

给定带权有向图G=(V,E),其中每条边的权是非负实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到所有其它各顶点的最短路长度。这里路的长度是指路上各边权之和。
在这里插入图片描述

2.2 图解求解过程

用优先队列式分支限界法解有向图G的单源最短路径问题产生的解空间树。其中,每一个结点内数字表示该结点所对应的当前路长
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3 编码实现

package com.zet.branch_and_bound;

import java.util.*;

/**
 * @program: arithmetic
 * @ClassName ShortestPath
 * @description:
 * @author: zhangzhifeng
 * @create: 2023-05-07 10:20
 * @Version 1.0
 * input:
 * 11 19
 * SA 2 SB 3 SC 4 AF 7 AB 3 AE 2 BE 9 BD 2 CD 2 FG 9 FH 1 EH 3 ED 1 DI 1 DH 5 GT 7 HT 8 IH 2 IT 2
 **/
public class ShortestPath {
    private static int N;   // 节点个数
    private static int EDGES;   // 边的数量
    private static float[][] adj;     // 邻接矩阵

    public static void main(String[] args) {
        Scanner scanner = initField();
        initVertix(scanner);
        printVertixEdgeAndWeight();
        int[] path = new int[N];        // path[i]:记录最佳路径中,i的上一个顶点是path[i]
        float[] dist = new float[N];    // dist[i]:从源点到当前顶点i的最短距离
        for (int j = 1; j < N; j++) {
            dist[j] = Float.MAX_VALUE;
        }
        calcShortestPath(0, adj, dist, path);
        printSourceToEndPointShortestPath(path);
        printSourceToEveryPointShortestDist(dist);
    }

    private static Scanner initField() {
        Scanner scanner = new Scanner(System.in);
        System.out.print("input the number of vertix and edge:");
        N = scanner.nextInt();
        EDGES = scanner.nextInt();
        adj = new float[N][N];
        return scanner;
    }

    private static void printVertixEdgeAndWeight() {
        System.out.println("边和权信息:");
        // 打印边权关系
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (adj[i][j] != Float.MAX_VALUE && adj[i][j] != 0 && i != j) {
                    char start = (char) ('A' + i - 1), end = (char) ('A' + j - 1);
                    if (i == 0) {
                        start = 'S';
                    }
                    if (j == N - 1) {
                        end = 'T';
                    }
                    System.out.println("(" + start + ", " + end + ") = " + adj[i][j]);
                }
            }
        }
    }

    private static void initVertix(Scanner scanner) {
        for (int i = 0; i < adj.length; i++) {
            for (int j = 0; j < adj.length; j++) {
                adj[i][j] = Float.MAX_VALUE;
            }
        }
        System.out.print("please input vertix and weight:");
        for (int i = 0; i < EDGES; i++) {
            String vertix = scanner.next();
            int startPos = vertix.charAt(0) - 'A' + 1, targetPos = vertix.charAt(1) - 'A' + 1;
            if (vertix.charAt(0) == 'S') {
                startPos = 0;
            }
            if (vertix.charAt(1) == 'T') {
                targetPos = N - 1;
            }
            float weight = scanner.nextFloat();
            adj[startPos][targetPos] = weight;
        }
    }

    private static void printSourceToEndPointShortestPath(int[] path) {
        System.out.print("最小堆求解的路径为:");
        // TODO 10 9 4 2 0
        List<Integer> list = new ArrayList<>();
        list.add(N - 1);
        while (true) {
            if (path[list.get(list.size() - 1)] == 0) {
                list.add(0);
                break;
            }
            list.add(path[list.get(list.size() - 1)]);
        }

        for (int j = list.size() - 1; j >= 0; j--) {
            if (list.get(j) == 0) {
                System.out.print("S-->");
            } else {
                char c = (char) (list.get(j) + 'A' - 1);
                if (j != 0) {
                    System.out.print(c + "-->");
                } else {
                    System.out.println('T');
                }
            }
        }
    }

    private static void printSourceToEveryPointShortestDist(float[] dist) {
        System.out.print("从顶点S到各顶点最短距离:");
        for (int i = 1; i < dist.length - 1; i++) {
            char end = (char) ('A' + i - 1);
            System.out.print("dist[" + end + "] = " + dist[i] + " ");
        }
        System.out.println("dist[" + 'T' + "] = " + dist[10]);
    }

    private static void calcShortestPath(int v, float[][] adj, float[] dist, int[] path) {
        HeapNode node = new HeapNode(v, 0);
        PriorityQueue<HeapNode> pq = new PriorityQueue<>();
        pq.offer(node);// 起始结点入堆

        while (true) {
            // 搜索问题的解空间
            for (int i = 1; i < path.length; i++) {
                // 判断从源点出发经过当前结点到达当前结点的下一个结点的距离是否小于解空间树中从源点出发经过其它结点到达当前结点的下一个结点的距离
                if ((adj[node.vIdx][i] < Float.MAX_VALUE) && (node.step + adj[node.vIdx][i]) < dist[i]) {
                    dist[i] = node.step + adj[node.vIdx][i];// 记录从源结点到node结点的最短距离
                    path[i] = node.vIdx;
                    HeapNode heapNode = new HeapNode(i, dist[i]);
                    pq.offer(heapNode);// 结点入堆
                }
            }
            if (pq.isEmpty()) {
                break;
            } else {
                // 移除堆顶元素
                node = pq.poll();
            }
        }
    }

    private static class HeapNode implements Comparable {
        private int vIdx;  // 顶点编号
        private float step;   // 当前路长

        public HeapNode(int vIdx, float step) {
            this.vIdx = vIdx;
            this.step = step;
        }

        @Override
        public int compareTo(Object o) {
            float oLen = ((HeapNode) o).step;
            if (step < oLen) {
                return -1;
            } else if (step > oLen) {
                return 1;
            } else {
                return 0;
            }
        }
    }
}

2.4 运行结果

input the number of vertix and edge:11 19
please input vertix and weight:SA 2 SB 3 SC 4 AF 7 AB 3 AE 2 BE 9 BD 2 CD 2 FG 9 FH 1 EH 3 ED 1 DI 1 DH 5 GT 7 HT 8 IH 2 IT 2
边和权:
(S, A) = 2.0
(S, B) = 3.0
(S, C) = 4.0
(A, B) = 3.0
(A, E) = 2.0
(A, F) = 7.0
(B, D) = 2.0
(B, E) = 9.0
(C, D) = 2.0
(D, H) = 5.0
(D, I) = 1.0
(E, D) = 1.0
(E, H) = 3.0
(F, G) = 9.0
(F, H) = 1.0
(G, T) = 7.0
(H, T) = 8.0
(I, H) = 2.0
(I, T) = 2.0
最小堆求解的路径为:S-->B-->D-->I-->T
从顶点S到各顶点最短距离:dist[A] = 2.0 dist[B] = 3.0 dist[C] = 4.0 dist[D] = 5.0 dist[E] = 4.0 dist[F] = 9.0 dist[G] = 18.0 dist[H] = 7.0 dist[I] = 6.0 dist[T] = 8.0
Disconnected from the target VM, address: '127.0.0.1:14736', transport: 'socket'

Process finished with exit code 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值