有边数限制且存在负权值的最短路

dijstra算法基于贪心思想,当有负权边时,局部最优不一定是全局最优,所以采用bellman - ford算法

Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在 n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。
(通俗的来讲就是:假设 1 号点到 n 号点是可达的,每一个点同时向指向的方向出发,更新相邻的点的最短距离,通过循环 n-1 次操作,若图中不存在负环,则 1 号点一定会到达 n 号点,若图中存在负环,则在 n-1 次松弛后一定还会更新)

具体步骤

bellman - ford算法的具体步骤 for n次 for 所有边 a,b,w (松弛操作) dist[b] = min ( dist[b] , back[a] + w)
注意:back[] 数组是上一次迭代后 dist[] 数组的备份,由于是每个点同时向外出发,因此需要对 dist[]
数组进行备份,若不进行备份会因此发生串联效应,影响到下一个点

注意

是否能到达n号点的判断中需要进行 if ( dist[n] > inf/2 ) 判断,而并非是 if( dist[n] == INF)
判断原因是INF是一个确定的值,并非真正的无穷大,会随着其他数值而受到影响,dist[n]大于某个与INF相同数量级的数即可
bellman - ford算法擅长解决有边数限制的最短路问题
时间复杂度 O(nm)

C++代码

#include<bits/stdc++.h>
using namespace std;
const int N = 510;
const int M = 1e5+10;
const int inf = 0x3f3f3f3f;
struct node{
	int a;int b;int w;
}e[M];   //边
//  距离   备份 
int dis[N],back[N],n,m,k;
int bellman_ford(){
	fill(dis,dis+N,inf);
	//memset(dis,0x3f,sizeof(dis));      memset和fill有区别 今天才知道 
	//cout<<dis[0]<<endl;
	dis[1] = 0;
	for(int i = 0;i < k;i++){		//k次循环 
		memcpy(back,dis,sizeof(dis));
		for(int j = 0;j < m;j++){
			int a = e[j].a , b = e[j].b, w = e[j].w;
			dis[b] = min(dis[b],back[a] + w);
			//避免给a更新后立马更新b, 这样b一次性最短路径就多了两条边出来
		}
	}
	if(dis[n] > inf/2) cout<<"impossible";
	else cout<<dis[n];
}
int main(){
	cin>>n>>m>>k;
	for(int i = 0 ;i < m;i++){
		cin>>e[i].a>>e[i].b>>e[i].w;
	}
	bellman_ford();
} 

Java代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
    static int N = 510;
    static int M = 100010;
    static int n;//总点数
    static int m;//总边数
    static int k;//最多经过k条边
    static int[] dist = new int[N];//从1到点到n号点的距离
    static Node[] list = new Node[M];//结构体
    static int inf = 0x3f3f3f3f;
    static int[] back = new int[N];
    public static void bellman_ford(){
        Arrays.fill(dist, INF);
        dist[1] = 0;
        for(int i = 0;i < k;i++){
            back = Arrays.copyOf(dist, n + 1);
            for(int j = 0;j < m;j++){
                Node node = list[j];
                int a = node.a;
                int b = node.b;
                int c = node.c;
                dist[b] = Math.min(dist[b], back[a] + c);
            }
        }
        if(dist[n] > inf/2) System.out.println("impossible");
        else System.out.println(dist[n]);
    }
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] str1 = reader.readLine().split(" ");
        n = Integer.parseInt(str1[0]);
        m = Integer.parseInt(str1[1]);
        k = Integer.parseInt(str1[2]);
        for(int i = 0;i < m;i++){
            String[] str2 = reader.readLine().split(" ");
            int a = Integer.parseInt(str2[0]);
            int b = Integer.parseInt(str2[1]);
            int c = Integer.parseInt(str2[2]);
            list[i] = new Node(a,b,c);
        }
        bellman_ford();

    }

}
class Node
{
    int a, b, c;
    public Node(int a,int b,int c)
    {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠风丶北枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值