——————————————————————————————————————————————————
第一次寫最短路徑留念,順便小結一下模板。
最短路有諸多算法,如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]);
}
}