最小路(Bellman-Ford算法)(负权情况)

本文详细介绍Bellman-Ford算法的应用场景及实现细节,解决含有负权重边的有向图最短路径问题,并提供Java代码示例。

问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式
第一行两个整数n, m。

接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入
3 3
1 2 -1
2 3 -1
3 1 2
样例输出
-1
-2
数据规模与约定
对于10%的数据,n = 2,m = 2。

对于30%的数据,n <= 5,m <= 10。

对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。

喜欢这种直接的题,没有任何修饰,直切主题

所以作为一道纯粹介绍Bellman-Ford算法的题来解决图中如果含有负边的情况,也因而往往出现在有向图中,(假设是有向图且存在负边,那么我们只要一直在这条边上来回移动,便会一直趋向于无穷小)
直接上代码:

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


public class Main 
{   

    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();

        Edge list[]=new Edge[m];//需要记录m条边的信息
        for(int i=0;i<m;i++)
        {
            list[i]=new Edge(sc.nextInt(),sc.nextInt(),sc.nextInt());
        }
        int d[]=new int[n+1];
        for(int i=1;i<=n;i++)d[i]=Integer.MAX_VALUE;
        d[1]=0;//求的是从1到其他所有点的最短路
        for(int k=0;k<n-1;k++)
            for(int i=0;i<m;i++)
            {
                int x=list[i].from;
                int y=list[i].to;
                if(d[x]<Integer.MAX_VALUE)//必须是从之前经过处理的点开始
                    d[y]=Math.min(d[y], d[x]+list[i].w);
            }
        for(int i=2;i<=n;i++)
            System.out.println(d[i]);
    }
}
class Edge
{
    int from;//起点
    int to;//终点
    int w;
    Edge(int from,int to,int w)
    {
        this.from=from;
        this.w=w;
        this.to=to;
    }
}

可以思考一下这个算法与Dijkstra算法的区别(详情见上一篇)在Dijstra算法中有一个mark数组的标志量用来观测 一个点是否经过处理,但是这里没有,其原因就是有负权的存在,假设,存在一个负值极大的一条边(但不构成负环)且里这个点非常远,那么即使如此,我们也是需要去考虑这条边,因为 加入了这条边可能会使整个最短路跟小,所以每次我们都需要去遍历边。所以不需要mark。

优化:用队列代替循环检查

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;



public class Main 
{   
    static ArrayList<Edge>list[];
    static int n,m,d[],inq[],cnt[],p[];
    public static void main(String[] args) 
    {
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        d=new int[n+1];
        inq=new int[n+1];
        cnt=new int[n+1];
        //p=new int[n+1];
        list=(ArrayList<Edge>[])new ArrayList[n+1];
        for(int i=0;i<m;i++)
        {
            int u=sc.nextInt();
            int v=sc.nextInt();
            int w=sc.nextInt();
            if(list[u]==null)list[u]=new ArrayList<>();
            list[u].add(new Edge(v,w));//建图
        }
    }
    static boolean BellmanFord(int s)
    {
        Queue<Integer> queue=new LinkedList<>();
        for(int i=1;i<=n;i++)
            d[i]=Integer.MAX_VALUE;
        d[s]=0;
        inq[s]=1;
        queue.add(s);
        while(!queue.isEmpty())
        {
            int x=queue.remove();
            inq[x]=0;

            for(int i=0;i<list[x].size();i++)
            {
                Edge e=list[x].get(i);
                if(d[x]<Integer.MAX_VALUE&&d[e.to]>d[x]+e.w)
                {
                    d[e.to]=d[x]+e.w;
                    if(inq[e.to]==0)
                    {
                        queue.add(e.to);
                        inq[e.to]=1;
                        if(++cnt[e.to] > n)
                        {
                            return false;
                        }
                    }
                }
            }

        }
        return true;
    }
}
class Edge//如果需要建图就不需要from
{

    int to;
    int w;
    Edge(int to,int w)
    {
        this.w=w;
        this.to=to;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值