Bellman-Ford演算法
阿新 • • 發佈:2021-02-02
技術標籤:演算法筆記
Bellman-Ford演算法
Bellman-Ford演算法:求解有負權邊的最短路徑問題,也可以解決單源最短路徑問題。
環:從某個頂點出發、經過若干個不同的頂點之後可以回到該頂點的情況。
零環、正環、負環:環A->B->C中的邊權之和分別為0、正、負。
由於零環和正環的存在不能使最短路徑更短,則不會影響最短路徑。而有負環,且從源點可以到達,則會影響最短路徑的求解。但如果負環無法從源點出發到達,則最短路徑的求解不會受到影響。
Bellman-Ford演算法設定一個數組d,用來存放從源點到達各個頂點的最短距離。
同時Bellman-Ford演算法返回一個bool值:如果其中存在從源點可達的負環,那麼函式返回false;否則,函式將返回true,此時陣列d中存放的值就是從源點到達各頂點的最短距離。
Bellman-Ford演算法主要思路:對圖中的邊進行V-1輪操作,每輪都遍歷圖中的所有邊;對每條邊u->v,如果以u為中介點可以使d[v]更小,即d[u]+length[u->v]<d[v]成立時,就用d[u]+length[u->v]更新d[v]。
for(i = 0; i < n - 1; i++) //執行n-1輪操作,其中n為頂點數
{
for(each edge u->v) //每輪操作都遍歷所有邊
{
if(d[u] + length[u->v] < d[v]) //以u為中介點可以使d[v]更小
{
d[v] = d[u] + length[u->v]; //鬆弛操作
}
}
}
如果圖中沒有從源點可達的負環,那麼陣列d中的所有值都應當已經達到最優。因此,只需要再對所有邊進行一輪操作,判斷是否有某條邊u->v仍然滿足d[u]+length[u->v]<d[v],如果有,則說明圖中有從源點可達的負環,返回false;否則,說明陣列d中的所有值都已經達到最優。
for(each edge u->v) //對每條邊進行判斷
{
if(d[u] + length[u->v] < d[v]) //如果仍可以被鬆弛
{
return false; //說明圖中有從源點可達的負環
}
return true; //陣列d的所有值都已經達到最優
}
Bellman-Ford演算法遍歷所有邊,使用鄰接表比較方便
struct Node
{
int v, dis; //v為鄰接邊的目標頂點,dis為鄰接邊的邊權
}
vector<Node> Adj[MAXV]; //圖G的鄰接表
int n; //n為頂點數,MAXV為最大頂點數
int d[MAXV]; //起點到達各點的最短路徑長度
bool Bellman(int s) //s為源點
{
fill(d, d + MAXV, INF); //fill函式將整個d陣列賦為INF
d[s] = 0; //起點s到達自身的距離為0
//以下為求解陣列d的部分
for(int i = 0; i < n - 1; i++) //執行n-1輪操作,n為頂點數
{
for(int u = 0; u < n; u++) //每輪操作都遍歷所有邊
{
for(int j = 0; j < Adj[u].size(); j++)
{
int v = Adj[u][j].v; //鄰接邊的頂點
int dis = Adj[u][j].dis; //鄰接邊的邊權
if(d[u] + dis < d[v]) //以u為中介點可以使d[v]更小
{
d[v] = d[u] + dis; //鬆弛操作
}
}
}
}
//以下為判斷負環的程式碼
for(int u = 0; u < n; u++) //對每條邊進行判斷
{
for(int j = 0; j < Adj[u].size(); j++)
{
int v = Adj[u][j].v; //鄰接邊的頂點
int dis = Adj[u][j].dis; //鄰接邊的邊權
if(d[u] + dis < d[v]) //如果仍可以被鬆弛
{
return false; //說明圖中有從源點可達的負環
}
}
}
return true; //陣列d的所有值都已經達到最優
}