问题描述
给定一个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;
}
}
本文详细介绍Bellman-Ford算法的应用场景及实现细节,解决含有负权重边的有向图最短路径问题,并提供Java代码示例。
835

被折叠的 条评论
为什么被折叠?



