第一种做法: djkstra做法
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=105;
const int inf=0x3f3f3f;
int mapp[maxn][maxn];
int vis[maxn];//区分结点在S中还是V-S中
int dis[maxn];//最短路
int n,m,a,b,c;
void init()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
mapp[i][j]=inf;
memset(vis,0,sizeof(vis));
}
void djkstra(int x,int n)//以x为源点,以n为终点的最短路
{
for(int i=1; i<=n; i++)
dis[i]=mapp[x][i];
vis[x]=1;//S中初始的点只有x结点
for(int i=1; i<=n; i++)
{
int minx=inf;
int p;
for(int j=1; j<=n; j++)
if(!vis[j]&&dis[j]<minx)
{
p=j;
minx=dis[j];
}//将V-S中的最短路径求出,用dis[p]表示
vis[p]=1;//使p点进入S集合
for(int j=1;j<=n;j++)
if(!vis[j]&&dis[j]>dis[p]+mapp[p][j])
dis[j]=dis[p]+mapp[p][j];
}
}
int main()
{
while(cin>>n>>m)
{
if(!m&&!n)break;
init();
for(int i=0; i<m; i++)
{
cin>>a>>b>>c;
mapp[a][b]=mapp[b][a]=c;
}
djkstra(1,n);
cout<<dis[n]<<endl;
}
return 0;
}
第二种做法:弗洛伊德做法
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
const int inf=0x3f3f3f;
int dis[maxn][maxn];
int n,m,p,q,t;
void init()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dis[i][j]=inf;
}
void floyd()
{
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0)
break;
init();
for(int i=0; i<m; i++)
{
cin>>p>>q>>t;
dis[p][q]=dis[q][p]=t;
}
floyd();
cout<<dis[1][n]<<endl;
//已经求得任意两点间的最短路径
}
return 0;
}
总结:djkstra算法的求解过程:
对于网络N=(V,E)中的顶点V划分为两部分:
第一部分S:表示已经求出最短路径的顶点集合,初始时只包含源点v0
第二部分V-S:没有求出最短路径的顶点集合,初始时为集合V-{v0}
算法的操作内容:按照v0到每个顶点最短路径长度递增的顺序,将集合V-S中的顶点逐个加入S中。
在操作的过程中,总保证v0到S中各顶点的长度始终不大于v0到V-S中各顶点的路径长度。
下面证明这种求解方式的正确性:
假设S是已经求得最短路径顶点的集合,假设下一条我们要求的最短路径终点是x
可以证明:下一条最短路径只有下面两种情况而不会出现第三种情况:
1.边(v0,x)
2.v0经过S集合中的顶点最后到达x的路径
我们可以用反证法证明:假设出现第三种情况:v0经若干顶点到达x的最短路径上有一个顶点v在集合V-S中:
这说明存在一个V-S集合中的顶点v使v0-v的路径长度小于v0-x的路径长度,这是不可能的。因为算法是按照
路径长度递增的顺序产生最短路径的,所以长度比v0-x小的路径都已经产生,且顶点v必定在集合S中。
算法的正确性证明完毕。
第三种做法:SPFA
这种算法以队列的形式改善了Bellman-frod算法,降低了时间复杂度,写法上和BFS类似,不过需要结点出队后标记数组重新赋值为0
利用访问邻接点的方式进行对其他结点的更新,进行松弛操作,直到所有节点都达到最短路径为止
下面是AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=105;
const int inf=0x3f3f3f3f;
int mapp[maxn][maxn];
int dis[maxn];
int vis[maxn];
int n,m;
queue<int>que;
void init()
{
memset(vis,0,sizeof(vis));
memset(mapp,inf,sizeof(mapp));
memset(dis,inf,sizeof(dis));
}
void spfa()
{
vis[1]=1;
dis[1]=0;
que.push(1);
while(!que.empty())
{
int pp=que.front();
que.pop();
vis[pp]=0;//由于结点不一定达到最短路径,所以标志数组需要赋值为0
for(int i=1; i<=n; i++)
if(dis[i]>dis[pp]+mapp[pp][i])
{
dis[i]=dis[pp]+mapp[pp][i];
if(!vis[i])
{
que.push(i);
vis[i]=0;
}
}
}
}
int main()
{
int a,b,t;
while(scanf("%d%d",&n,&m),n+m)
{
init();
for(int i=0; i<m; ++i)
{
scanf("%d%d%d",&a,&b,&t);
if(t<mapp[a][b])
mapp[a][b]=mapp[b][a]=t;
}
spfa();
printf("%d\n",dis[n]);
}
return 0;
}