最短路徑 模板小結

——————————————————————————————————————————————————

第一次寫最短路徑留念,順便小結一下模板。

最短路有諸多算法,如SPFA、Dijketra、floyd……這裏介紹兩種最常用的模板Dijketra和floyd 模板。

Dijketra

請忽略字醜等自然因素

————————————————————————————————————
相當於從出發點開始,每次選取最短的那條邊,把邊的另一個端點加入集合(並在vis[]中標記爲已加入),並更新能“碰得到”的點到原點的距離 dis[],如此不斷更新,直到到達目標所在的位置。

模板如下:

void Dij()
{
    memset(vis,0,sizeof(vis));
    for( int i=2; i<=n; i++ )
    dis[i] = par[1][i];
    dis[1] = 0;
    vis[1] = 1;
    for( int i=0; i<n; i++ )
    {
        int temp= INF; 
        int v = -1; // 定義一個-1 ,方便判斷是不是有邊的存在(或邊是否已全部加入 已連接點 的集合)
        for( int j=1; j<=n; j++ ) // 由於每一個點都要進行一遍邊的判定,所以是兩個for 
        {
            if( temp > dis[j] && !vis[j] )
            {
                temp = dis[j];
                v = j;
            }
        } 
        if( v == -1 )  return; //如果全部邊加完了(或者沒有邊),直接退出
        vis[v] = 1;
        for( int j=1; j<=n; j++ )// 更新 
        {
            if( !vis[j] && dis[j] > dis[v]+par[v][j] )
                dis[j] = dis[v]+par[v][j];
        } 
    }   
}

————————————————————————————————————————————————

Flody

和dijkstra算法比起來,這個就要簡單暴力多了,基本上是水題都能過,正經題目基本Time Limit Exceeded的三個for 暴力算法 ,時間複雜度O(N^3)。

算法核心是直接通過比較 從 A - > B 與 從 A - > D1 - > D2 …… Dn- >B,之間的距離大小,並把最短的邊逐一標記,全部記錄,當查詢從A ->B的時候就直接取出。

算法的優點是 不用像dijkstra一樣要從原點出發,用起來特別好用。缺點是爆的簡直不要太爽。
同一道題改了N次才用Floyd水過……
這裏寫圖片描述

void floyd()
{
    for( int k=1; k<=n; k++ )  // 中間點 
        for( int j=1; j<=n; j++ )
            for( int i=1; i<=n; i++ )
            {
                par[i][j] = min( par[i][j],par[k][j]+par[i][k] );
            }
}

————————————————————————————————————————————————

下面附帶一道特別水的模板題:

最短路

Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 54559 Accepted Submission(s): 24052

Problem Description
在每年的校賽裏,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?

Input
輸入包括多組數據。每組數據第一行是兩個整數N、M(N<=100,M<=10000),N表示成都的大街上有幾個路口,標號爲1的路口是商店所在地,標號爲N的路口是賽場所在地,M則表示在成都有幾條路。N=M=0表示輸入結束。接下來M行,每行包括3個整數A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A與路口B之間有一條路,我們的工作人員需要C分鐘的時間走過這條路。
輸入保證至少存在1條商店到賽場的路線。

Output
對於每組輸入,輸出一行,表示工作人員從商店走到賽場的最短時間

Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0

Sample Output
3
2

Source
UESTC 6th Programming Contest Online

Recommend

————————————————————————————————————————————

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int MAX_N = 1010; 
int par[MAX_N][MAX_N];
int dis[MAX_N];
int vis[MAX_N];
int n,m; 

void floyd()
{
    for( int k=1; k<=n; k++ )  // 中間點 
        for( int j=1; j<=n; j++ )
            for( int i=1; i<=n; i++ )
            {
                par[i][j] = min( par[i][j],par[k][j]+par[i][k] );
            }
}

void Dij()
{
    memset(vis,0,sizeof(vis));
    for( int i=2; i<=n; i++ )
    dis[i] = par[1][i];
    dis[1] = 0;
    vis[1] = 1;
    for( int i=0; i<n; i++ )
    {
        int temp= INF; 
        int v = -1; // 定義一個-1 ,方便判斷是不是有邊的存在,更有通用性。
        for( int j=1; j<=n; j++ ) // 由於每一個點都要進行一遍邊的判定,所以是兩個for 
        {
            if( temp > dis[j] && !vis[j] )
            {
                temp = dis[j];
                v = j;
            }
        } 
        if( v == -1 )  return; //如果沒有邊,直接退出
        vis[v] = 1;
        for( int j=1; j<=n; j++ )// 更新,就像站在路口目測距離 
        {
            if( !vis[j] && dis[j] > dis[v]+par[v][j] )
                dis[j] = dis[v]+par[v][j];
        } 
    }   
}


int main()
{
    int a,b,l;
    while( ~scanf("%d%d",&n,&m),m||n )
    {
        for( int i=1; i<=n; i++ )
            for( int j=1; j<=n; j++ )
            {
                par[i][j] = INF;
            }
        for( int i=0; i<m; i++ )
        {
            scanf("%d%d%d",&a,&b,&l);
            if( par[a][b] > l )
            par[a][b] = par[b][a] = l;
        }
        //floyd();
        Dij();
        printf("%d\n",dis[n]);
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章