单源最短路径问题——分支限界法(Java)

这篇具有很好参考价值的文章主要介绍了单源最短路径问题——分支限界法(Java)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、 前置芝士

1.1 分支限界法求解目标

分支限界法与回溯法的不同求解目标:

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

1.2 分支限界法引言

分支限界法与回溯法的不同搜索方式:

  • 回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
  • 分支限界法的搜索策略:在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展对点。为了有效地选择下一扩展结点,以加速搜索的进程,在每一活结点处,计算一个函数值(限界),并根据这些已计算出的函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间树上有最优解的分支推进,以便尽快地找出一个最优解

1.3 分支限界法基本思想

  • 分支限界法通常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
  • 问题的解空间树是表示问题解空间的一棵有序树,常见的有子集树排列树

1.4 两种典型的解空间树

子集树(Subset Trees):

当所给问题是从n个元素的集合中找出满足某种性质的子集时,相应的解空间树称为子集树。在子集树中,|S0|=|S1|=…=|Sn-1|=c,即每个结点有相同数目的子树,通常情况下c=2,所以,子集树中共有2n个叶子结点,因此,遍历子集树需要O(2n)时间。

排列树(Permutation Trees):

当所给问题是确定n个元素满足某种性质的排列时,相应的解空间树称为排列树。在排列树中,通常情况下,|S0|=n,|S1|=n-1,…,|Sn-1|=1,所以,排列树中共有n!个叶子结点,因此,遍历排列树需要O(n!)时间。

2、分支限界法解题过程

2.1 算法要点

  • 在分支限界法中,每一个活结点只有一次机会成为扩展结点
  • 活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。
  • 从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程
  • 这个过程一直持续到找到所求的解或活结点表为空时为止。

活结点表:具有先进先出的性质,是队列

2.2 两个重要机制

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

2.3 适用范围

  • 分支限界法类似于回溯法,有一些问题其实无论用回溯法还是分支限界法都可以得到很好的解决,但是另外一些则不然。
  • 下表列出了回溯法和分支限界法的一些区别:

单源最短路径问题——分支限界法(Java)

2.4 两种方式

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

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

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

常用堆来实现优先队列

3、单源最短路径问题

3.1 问题描述

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

单源最短路径问题——分支限界法(Java)

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

3.2 图解题目

单源最短路径问题——分支限界法(Java)

单源最短路径问题——分支限界法(Java)

单源最短路径问题——分支限界法(Java)

单源最短路径问题——分支限界法(Java)

单源最短路径问题——分支限界法(Java)

4、程序代码

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Scanner;


/**
 * TODO
 *   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 t1 {
    static int N;   // 节点个数
    static int EDGES;   // 边的数量
    static float[][] adj;     // 邻接矩阵

    public static void main(String[] args) {

        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 + 1][N + 1];

        for (int i = 0; i < adj.length; i++) {
            for (int j = 0; j < adj.length; j++) {
                adj[i][j] = Float.MAX_VALUE;
            }
        }

        System.out.println("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;
        }


        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]);
//                    System.out.println("(" +  i + ", " + j  + ") = " + adj[i][j]);
                }
            }
        }

        int[] path = new int[N + 1];        // path[i]:记录最佳路径中,i的上一个顶点是path[i]
        float[] dist = new float[N + 1];    // dist[i]:从源点到当前顶点i的距离
        int vertix = 0;

        for (int j = 1; j <= N; j++) {
            dist[j] = Float.MAX_VALUE;
        }
        shortest(vertix, adj, dist, path);


        System.out.print("最小堆求解的路径为:");
//        for (int i = 1; i < path.length; i++) {
//            System.out.println(path[i]);
//        }
//        System.out.println("------------------------");

        // TODO 10 9 4 2 0
        List<Integer> list = new ArrayList<>();
        list.add(N - 1);
        list.add(path[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 i = 0; i < list.size(); i++) {
//            System.out.println(list.get(i));
//        }
//        System.out.println("-------------------------");
        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');
                }
            }
        }


        System.out.println("从顶点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]);
    }

    public static void shortest(int v, float[][] adj, float[] dist, int[] path) {
        int n = path.length - 1;
        HeapNode enode = new HeapNode(v, 0);
        PriorityQueue<HeapNode> pq = new PriorityQueue<>();
        pq.offer(enode);

        while (!pq.isEmpty()) {
            HeapNode pollNode = pq.poll();
            int start = pollNode.vIdx;
            float currStep = pollNode.step;
            // 搜索问题的解空间
            for (int i = 1; i <= n; i++) {
                if (adj[start][i] <= Float.MAX_VALUE && pollNode.step + adj[start][i] < dist[i]) {
                    dist[i] = currStep + adj[start][i];
                    path[i] = start;
                    HeapNode node = new HeapNode(i, dist[i]);
                    pq.offer(node);
//                    System.out.println(start + "-->" + i);
                }
            }
        }
    }

    static class HeapNode implements Comparable {
        int vIdx;  // 顶点编号
        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;
            }
            if (step == oLen) {
                return 0;
            }
            return 1;
        }
    }
}
复制代码

其中,shorest方法的while()代码块可以用以下代码替换

while (true) {
        // 搜索问题的解空间
        for (int i = 1; i <= n; i++) {
            if (adj[enode.vIdx][i] < Float.MAX_VALUE && enode.step + adj[enode.vIdx][i] < dist[i]) {
                dist[i] = enode.step + adj[enode.vIdx][i];
                path[i] = enode.vIdx;
                HeapNode node = new HeapNode(i, dist[i]);
                pq.offer(node);
            }
        }
        if (pq.isEmpty()) {
            break;
        } else {
            // 移除
            enode = (HeapNode) pq.poll();
        }
    }
复制代码

运行结果

单源最短路径问题——分支限界法(Java)

5、参考资料

  • 算法设计与分析(第四版)

结束!

作者:7_
原文链接:https://juejin.cn/post/7176061385477423162
 文章来源地址https://www.toymoban.com/news/detail-455531.html

到了这里,关于单源最短路径问题——分支限界法(Java)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【图论】单源最短路

    算法提高课笔记 最短路问题可以分为以下两类: 边权非负——朴素Dijkstra、堆优化Dijkstra 有负权边——Bellman-Ford、SPFA 热浪 原题链接 德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!! 他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品

    2024年02月14日
    浏览(43)
  • 【图论 单源最短路】100276. 最短路径中的边

    单源最短路 图论知识汇总 给你一个 n 个节点的无向带权图,节点编号为 0 到 n - 1 。图中总共有 m 条边,用二维数组 edges 表示,其中 edges[i] = [ai, bi, wi] 表示节点 ai 和 bi 之间有一条边权为 wi 的边。 对于节点 0 为出发点,节点 n - 1 为结束点的所有最短路,你需要返回一个长度

    2024年04月22日
    浏览(34)
  • C++算法:单源最短路径Dijkstra

    如果你有一份北京地图,想从中关村走到三元桥,那么怎样能找出实现这一目的的最短路径呢?一种可能的方法就是将这两点之间所有的路线都找出来,然后求出每条路线的距离,找出最短的路线。但是仔细想想我们就会发现这种办法几乎是不可行的,因为这样的路线太多了,

    2024年02月09日
    浏览(45)
  • 算法提高-图论-单源最短路的扩展应用

    多源点单终点最短路建图: 创建虚拟源点(创建虚拟源点的时候以是spfa为例 可以在建图的时候建出来,也可以在spfa这直接入队,也是虚拟源点的意思) 反向建图变成单源点多终点,然后遍历终点的dist即可找出最短路 这题挺简单的就不详细说了,主要是第一次遇到计数问题

    2024年02月16日
    浏览(48)
  • 【算法】单源最短路径算法——Dijkstra算法

    迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。这是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用 贪心算法 的策略, 每次遍历到始点距离最

    2024年02月05日
    浏览(46)
  • 算法提高-图论-单源最短路的综合应用

    多次dijkstra求每个点到其它点的最短距离, 此时相当于建好了一张图,每个点之间的最短距离都知道了,接下来dfs搜一下怎么走最短即可 一篇博客解释了为什么一个正向建图求最小值,反向建图求最大值 根本思想是保证1到n的买卖是连通的

    2024年02月11日
    浏览(75)
  • 迪杰斯特拉算法 – 图的单源最短路径

    迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。迪杰斯特拉算法采

    2024年02月05日
    浏览(42)
  • 单源最短路(只有一个起点)bfs,多源BFS,目录力扣675.为高尔夫比赛砍树,多源最短路问题:力扣542.01矩阵力扣1020.飞地的数量

    目录 力扣675.为高尔夫比赛砍树 多源最短路问题: 力扣542.01矩阵 力扣1020.飞地的数量 Collections.sort(trees,(a,b)-{                    return f.get(a[0]).get(a[1])-f.get(b[0]).get(b[1]);                }); 这块比较难写 这段代码是Java中的一段代码,用于对名为trees的集合进行排序。这个集

    2024年04月12日
    浏览(67)
  • 【算法入门&搜索法】走迷宫|单源最短路径1

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:算法百炼成神 本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面

    2024年02月03日
    浏览(66)
  • 算法提高-图论-单源最短路的建图方式

    建图 找出一个牧场,它到其他牧场的距离之和最小 我是这么理解的,djsktra是一个贪心的思想,加法里面不能加负数我就不说了 求乘法最大值的时候为什么边权必须0-1,因为在乘法最大值里面有一个边权大于1的话那不就等价于求加法最小值的时候有一个边权为负数的么,d

    2024年02月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包