——————————————————————————————————————————————————
第一次写最短路径留念,顺便小结一下模板。
最短路有诸多算法,如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]);
}
}