1. 程式人生 > 其它 >Bellman-Ford演算法

Bellman-Ford演算法

技術標籤:演算法筆記

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的所有值都已經達到最優
}