單源最短路問題,就是對一個邊帶權圖G和其上一點s,求G上每一點到s的權最小路
性質: 1.最短路上不會有環
2.將達不到或者暫時達不到的點的距離習慣記做無窮
3.該問題滿足最優子結構,即最短路的子圖也爲最短路
如何記錄:對每個節點v,維護一個值v.pi,表示這個點的前驅節點。求解完成之後使用他構造最短路徑樹即可、
基本操作:
最短路徑估計:對每個點維護一個值v.d,爲當前估計的s到他的距離
初始化:將所有v.d置爲無窮,v.pi置爲null
鬆弛:對點u,v,若我們已經求出了u的最短路,且u,v鄰接,對比u.d+w(u+v)和v.d,若v.d比較大,說明從u到達v比v當前認爲的最短路更近,就可以將v.d置爲u.d+w(u+v),將v.pi置爲u
之後的算法就基於鬆弛操作進行
bellmanford算法
能夠解決帶負權圖的最短路問題,若圖上有負權迴路,返回false表示無解
這個算法的思路比較暴力:每次把每條邊鬆弛一次,重複v-1次。每次鬆弛完所有邊後可以保證滿足最短路性質的點距離原點的深度多了一層,所以重複v-1次後可以保證最短路已完成。
對於負權迴路的判斷:完成上面步驟後遍歷一遍每條邊,若還可以鬆弛,則說明有負權迴路。
優化:如果某次操作中沒有鬆弛,則說明之後也不會再鬆弛了,在這停止。
以hdu2544爲例
//其實這題並沒有用到bellmanford判負值的操作
#include <bits/stdc++.h>
using namespace std;
const int maxn=20000;
int vpi[200],vd[200],v,e;//前驅節點,最大值估計
struct edg
{
int vs,ve,w;
}ed[maxn];
void init()
{
memset(vpi,0,sizeof(vpi));
int cs,ce,cw;
for(int i=1;i<=v;i++) vd[i]=100000;
vd[1]=0;
for(int i=1;i<=e/2;i++)
{
cin>>cs>>ce>>cw;
ed[i*2-1].vs=cs;
ed[i*2-1].ve=ce;
ed[i*2-1].w=cw;
ed[i*2].vs=ce;
ed[i*2].ve=cs;
ed[i*2].w=cw;
}
}
bool bellmanford()
{
for(int i=1;i<=v;i++)
{
bool flag=true;
for(int j=1;j<=e;j++)
{
if(vd[ed[j].ve]>vd[ed[j].vs]+ed[j].w)
{
vd[ed[j].ve]=vd[ed[j].vs]+ed[j].w;
vpi[ed[j].ve]=ed[j].vs;
flag=false;
}
}
if(flag)break;
}
for(int i=1;i<=e;i++)
{
if(vd[ed[i].ve]>vd[ed[i].vs]+ed[i].w)
return false;
}
return true;
}
int main()
{
while(cin>>v>>e,v!=0)
{
e*=2;
init();
bellmanford();
cout<<vd[v]<<endl;
}
return 0;
}