2020-09-14

单源无向带权图 求最短路径问题

问题描述:

给定一个起点 s 和一个终点 e , 从s 到 e 的路线有很多 , 每个顶点间的花费都有 代价 cost。

求从 s 能够到达 e 的路径花费的最少代价 。

大致图像百度的一个:
在这里插入图片描述

Input : 输入格式:

第一行 m,n ,其中 m 表示顶点个数, n 表示边数。

接下来的 n 行 每行表示 u v cost, 即从 u 到 v 的代价 cost ,由于无向图,所以 也表示从 v 到 u 的代价cost。

最后一行 表示起点 s 和终点 e。

Output:输出格式:

一个数字, 表示起点 s 到终点 e 的 最小代价花费路径。

样例 :

 灵魂画手
    1
 / 5   \  6
2       3
\ 8    / 6
    4

样例1:
input:
4 4
1 2 5
1 3 6
2 4 8
3 4 6
1 4

output:
12

样例2:
4 4
1 2 25
1 3 18
2 4 28
3 4 22
1 4

output:
40

代码

提供一个基于 BFS 的解决方案(基于 迪杰斯特拉 的以后补。)

package june.code.byhehe.utils;

import java.util.*;

/**
 * 在有环无向带权图中 基于 BFS 求 S 到 E 的最短代价路径。
 * 步骤   构件图   ->    计算最短路径
 */
public class BuildNoDirectionWithWeightGraph {
    public List<int[]>[] graph;   // 路径图 ways 根据顶点数
    public int m;  // 节点数目
    public int[] arrived;  // 表示到达节点的 编号 和 代价   [顶点编号, cost]
    // 由于这里是使用 BFS 来完成 搜寻最小路径 因此 需要一个队列
    public Deque<int[]> deque;

    // 开始构建有环 无向带权图
//    public BuildNoDirectionWithWeightGraph(){}
    // 初始化

    public BuildNoDirectionWithWeightGraph(int m){
        this.m = m;
        this.graph = new List[m+1];
        // 需要进行创建并初始化
        for (int i = 0; i < m + 1; i++) {
            this.graph[i] = new ArrayList<>();
        }
        this.arrived = new int[m+1];
        // 初始化 权值到达数组
        Arrays.fill(this.arrived, Integer.MAX_VALUE);
        this.deque = new LinkedList<>();
    }

    // 构建形式 根据循环输入构建 构建形式 输入 : u v cost
    // 表示 从顶点 u 到 v 或者 v 到 u 的代价是 cost。
    public void buildGraph(int u, int v, int cost){
        // 注意是无向图 带权
        int[] uv = {v, cost};
        this.graph[u].add(uv);
        int[] vu = {u, cost};
        this.graph[v].add(vu);

    }

    // 这里要求算法的是最短路径 那么我们 这里通过 BFS 方法来进行计算
    public int ShortestCostRoadWithBFS(int start, int end){
        // 首先是构建队列 并将 节点 start 入 队列
        // 处理特殊情况
        if(start == end)
            return 0;
        // 先加入其实节点 此时 其花费为 0
        this.deque.push(new int[]{start, 0});
        while (!this.deque.isEmpty()){
            int[] poll = this.deque.poll();// 出队列
            // 然后 找到 当前顶点 所能关联到的 所有顶点 判断权值 并放入队列
            int curPost = poll[0];    // 当前节点
            int cost = poll[1];   // 到当前节点所花费的代价

            // 先判断 当前代价是否比 arrived[curPost] 更小
            if(cost < this.arrived[curPost]){
                // 表示目前的代价 比当前 arrived[curPost] 从其他节点到达 curPost 的累计代价更小 则进行更新
                this.arrived[curPost] = cost;   // 更新 当前代价  表示 到这个节点的 代价 从当前路径 花费更小
                // 然后 在判断当前节点所相连的节点是否都能放入 队列
                for(int[] uv : this.graph[curPost]){
                    // 首先判断当前节点 有没有被访问过 或者说 它的代价是否更小  node 形式 表示 节点 curPost 所能到的节点 和代价
                    // [v, cost]
                    if(cost + uv[1] < this.arrived[uv[0]]){   // this.arrived[curPost] 错误
                        // cost + uv[1] < this.arrived[uv[0]] 表示 从 u -> v 比 v 现在所能从其他路径到 v 花费的代价更小
                        // 也就是说 每一次 他都会选取一个更小的边(权值) 来进行 放入
                        this.deque.push(new int[]{uv[0], uv[1]+cost});
                        // 其中 uv[0] 表示 从当前curPost能到达的节点v(不止一个), uv[1]+cost 表示 花费的代价累积
                    }
                }
            }
        }
        return this.arrived[end];
    }

    // 测试数据
    /*
input:
第一行 m,n m 表示 节点数目, n 表示 边数
接下来 n 行表示 u v cost 即 节点 u 到 v 的花费代价
最后一行 s e 表示 起点和终点
input:
4 4
1 2 5
1 3 6
2 4 8
3 4 6
1 4
output:
一个数字 表示 起点 s 到终点 e 的最小化费代价
output:
12
*/
    public static class Test{
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int m = sc.nextInt();
            int n = sc.nextInt();
            // 初始化图
            BuildNoDirectionWithWeightGraph bgraph = new BuildNoDirectionWithWeightGraph(m);
            for (int i = 0; i < n; i++) {
                int u = sc.nextInt();
                int v = sc.nextInt();
                int cost = sc.nextInt();
                bgraph.buildGraph(u, v, cost);
            }
            int start = sc.nextInt();
            int end = sc.nextInt();
            int res = bgraph.ShortestCostRoadWithBFS(start, end);
            System.out.println(res);

        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值