最短路三大算法及其優化算法大總結+模板

最短路問題三大算法及其優化算法總結+模板

前言

這裏給了最短路問題中三大算法及其優化後的算法總結和模板,總結一下,以便後續學習。node

Floyd-Warshall

多源最短路,即要求求出圖中每兩個頂點之間的最短路。雖然Floyed的複雜度是$O(n^3)$,可是4行卻簡單不少,本質上是動態規劃算法。 思想:從i號頂點到j號頂點只通過前k號頂點的最短路徑。算法

const int inf=0x3f3f3f3f;
int Floyd()
{//初始化n個頂點 
    for(i = 1; i <= n; i ++)
        for(j = 1; j <= n; j ++)
            if(i == j)
                e[i][j] = INF;
            else
                e[i][j] = 0;
    for(k = 1; k <= n; k ++)//Floyd-Warshall算法核心語句 
        for(i = 1; i <= n; i ++)
            for(j = 1; j <= n; j ++)
                if(e[i][j] > e[i][k]+e[k][j])
                    e[i][j] = e[i][k]+e[k][j];
}

Bellman-Ford

Bellman-Ford算法能解決存在負權邊的單源點最短路徑問題。能夠檢測一個圖是否存在負權迴路:若是在進行了n-1輪鬆弛後,仍然能夠繼續成功鬆弛,說明存在負權邊。數組

const int inf=0x3f3f3f3f;
struct node{
    int from,to,w;
};
node edge[100];
bool Bellman(int s)
{
    for(i = 1; i <= n; i ++)
        dis[i] = inf;
    dis[s]=0;
    bool flag;
    for(i = 1; i <= n; i ++)
    {
        flag = false;
        for(j = 1; j <= m; j ++)
        {
            x = edge[j].from ;
            y = edge[j].to ;
            z = edge[j].w ;
            if(dis[y] > dis[x] + z)
            {
                dis[y] = dis[x] + z;
                flag = true;
            }
        }
        if(!flag) //最好加上這句話,很重要
            break;
        //若是更新到n遍,還可以繼續更新dis數組,說明存在負權環 
        if(flag&&i == n) 
            return false;//返回false表示這個圖存在負權環 
    }
    return true;//返回true表示求最短路成功 
}

SPFA 使用隊列優化的Bellman-Ford算法

每次實施一次鬆弛操做後,就會有一些頂點已經求得其最短路,此後這些頂點的最短路的估計值就會一直保持不變,再也不受到後面鬆弛的影響,可是每次還要判斷是否須要鬆弛,浪費了時間。學習

因此這裏每次僅僅對最短路的估計值發生了變化了的頂點的全部出邊執行鬆弛操做優化

//poj 3169 Layout 題解
const int inf=0x3f3f3f3f;
const int maxn=1e3+7;
struct edge
{
	int to, cost;
};
int dis[maxn];
bool inq[maxn]; //判讀一個點是否在隊列中
int cnt[maxn];
vector<edge> g[maxn];
queue<int> que;
int n, ml, md;
void init()
{
	for(int i=1; i<=n; i++)
	{
		g[i].clear();
		inq[i]=false;
		dis[i]=inf;
		cnt[maxn]=0;
	}
	while(!que.empty()) que.pop();
}
bool spfa(int s, int n)
{
	edge e;
	inq[s]=true;
	dis[s]=0;
	que.push(s);
	while(!que.empty())
	{
		int u=que.front();
		que.pop();
		inq[u]=false; //出隊,不在隊列中,賦值爲false;
		for(int i=0; i<g[u].size(); i++)
		{
			e=g[u][i];
			if(dis[e.to] > dis[u]+e.cost)
                
			{
				dis[e.to]=dis[u]+e.cost;
				if(!inq[e.to]) //若是以前沒有在隊列中,那麼就能夠入隊了。
				{
					inq[e.to]=true;
					que.push(e.to);
					cnt[e.to]++;
					if(cnt[e.to]>=n) return false;//說明有負環
				}
			}
		}
	}
	return true;//沒有負環,而且完成最短路	
}

Dijkstra

Dijkstra算法適合不含負權邊的單源最短路(單源最短路是指從源點到其他各個頂點的最短路徑)。 思想:每次找到離源點最近的一個頂點,而後以該頂點爲中心進行拓展,最終獲得源點到其他全部點的最短路徑spa

void dij(int s)
{
	for(int i=1; i<=n; i++)
		dis[i]=road[s][i];
	vis[s]=1;
    dis[s]=0; //這句話也能夠不加
	for(int i=1; i<n; i++)
	{
		int tmp=inf, k;
		for(int j=1; j<=n; j++)
		{
			if(!vis[j] && dis[j] < tmp)
			{
				tmp=dis[j];
				k=j;
			}
		}
		if(tmp==inf) //若是沒有更新成功,那麼接下來也不會再次更新,能夠結束循環了。
			break;//這句話之後就必須加上了。
		vis[k]=1;
		for(int j=1; j<=n; j++)
		{
			if(!vis[j] && dis[j] > dis[k]+road[k][j])
			dis[j]=dis[k]+road[k][j];
		}
	}
}

使用優先隊列+鄰接表優化的Dijkstra算法

//鄰接表+優先隊列優化 題目poj 2387 til the cows come home
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;
struct edge{
	int to, cost;
};
struct node{
	int d, u;
	bool friend operator < (const node a, const node b)
	{
		return a.d > b.d;
	}
};
int dis[maxn];
int vis[maxn];
vector<edge> g[maxn];
priority_queue<node> que;
int t, n;
void init()
{
	for(int i=1; i<=n; i++)
	{
		g[i].clear();
		vis[i]=0;
		dis[i]=inf;
	}
	while(!que.empty()) que.pop();
}
void dij(int s)
{
	int u;
	edge e;
	dis[s]=0;
	node tmp={0, s}, next;
	que.push(tmp);
	while(!que.empty())
	{
		tmp=que.top();
		que.pop();
		u=tmp.u;
        if(vis[u]==1) continue;
		vis[u]=1;
		for(int i=0; i<g[u].size(); i++)
		{
			e=g[u][i];
			if(!vis[e.to] && dis[e.to] > dis[u]+e.cost)//注意要加判斷是否訪問
			{
				dis[e.to]=dis[u]+e.cost;
				next.d=dis[e.to];
				next.u=e.to;
				que.push(next);
			}
		}
	}
}
相關文章
相關標籤/搜索