有邊數限制的最短路
題目:
給定一個n個點m條邊的有向圖,圖中可能存在重邊和自環, 邊權可能爲負數。
請你求出從1號點到n號點的最多經過k條邊的最短距離,如果無法從1號點走到n號點,輸出impossible。
注意:圖中可能 存在負權迴路 。
輸入格式
第一行包含三個整數n,m,k。
接下來m行,每行包含三個整數x,y,z,表示存在一條從點x到點y的有向邊,邊長爲z。
輸出格式
輸出一個整數,表示從1號點到n號點的最多經過k條邊的最短距離。
如果不存在滿足條件的路徑,則輸出“impossible”。
數據範圍
1≤n,k≤500,
1≤m≤10000,
任意邊長的絕對值不超過10000。
輸入樣例:
3 3 1
1 2 1
2 3 1
1 3 3
輸出樣例:
3
算法分析
1、問題:爲什麼Dijkstra不能使用在含負權的圖中?
分析:
若通過Dijkstra算法可以求出從1號點到達4號點所需的步數爲7。因爲diskstra算法的核心是選擇離源點最短距離的點更新其他點,從圖上可知Dijkstrea算法1號點只能選擇2號點更新其它位置,所以最短的負權變被忽略掉了,所以不可以用Dijkstra算法。
其實1號到4號點的最短距離是-2。
2、什麼是bellman - ford算法?
Bellman - ford算法是求含負權圖的單源最短路徑的一種算法,效率較低,代碼難度較小。其原理爲連續進行鬆弛,在每次鬆弛時把每條邊都更新一下,若在n-1次鬆弛後還能更新,則說明圖中有負環,因此無法得出結果,否則就完成。
(通俗的來講就是:假設1號點到n號點是可達的,每一個點同時向指向的方向出發,更新相鄰的點的最短距離,通過循環n-1次操作,若圖中不存在負環,則1號點一定會到達n號點,若圖中存在負環,則在n-1次鬆弛後一定還會更新)
3、bellman - ford算法的具體步驟
for n次
for 所有邊 a,b,w (鬆弛操作)
dist[b] = min(dist[b],back[a] + w)
注意:back[]數組是上一次迭代後dist[]數組的備份,由於是每個點同時向外出發,因此需要對dist[]數組進行備份,若不進行備份會因此發生串聯效應,影響到下一個點
4、是否能到達n號點的判斷中需要進行if(dist[n] > INF/2)判斷,而並非是if(dist[n] == INF)判斷,原因是INF是一個確定的值,並非真正的無窮大,會隨着其他數值而受到影響,dist[n]大於某個與INF相同數量級的數即可,1到3的距離並不是INF, 所以不能判斷是否=INF
5、bellman - ford算法擅長解決有邊數限制的最短路問題
時間複雜度 O(nm)O(nm)
其中n爲點數,m爲邊數 在這裏是O(km),k個點
AC代碼
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,k;
/*
有可能存在負權迴路,最短路不一定不存在了。 有可能是負無窮
for(n)
所有邊 a,b,w
遍歷所有的邊 m條邊
dist[n] = min(dist[b],dist[a]+w);
*/
const int N = 510;
const int M = 10010;
int dist[N], backup[N];
struct Edge
{
int a,b,w;
}edges[M];
int bellman_ford()
{
memset(dist, 0x3f, sizeof dist); //先給 dist上最大值
dist[1] = 0; //1到1點的最小距離初始化爲0
for(int i = 0; i < k; i++) // 不超過k次
{
memcpy(backup,dist, sizeof dist); //每一次將dist數組備份一份,防止出現串聯,更新的時候只用上一次出現的結果
for(int j = 0; j < m; j++){
int a = edges[j].a;
int b = edges[j].b;
int w = edges[j].w;
dist[b] = min(dist[b],backup[a] + w); // backup中存的就是上一次更新的結果
}
}
if(dist[n] > 0x3f3f3f3f/2) return -1;
return dist[n];
}
int main()
{
scanf("%d%d%d", &n,&m,&k);
for(int i = 0; i < m; i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w); // m個邊
edges[i] = {a,b,w};
}
int t = bellman_ford();
if(t == -1)
{
puts("impossible");
}
else
{
printf("%d\n",t);
}
}