一:Floyd-Warshall算法
求多源最短路(即任意两点间的最短路), 啊哈磊的讲解十分易懂,http://bbs.ahalei.com/thread-4554-1-1.html
直接贴出代码
#include <stdio.h>
int main()
{
int e[10][10],k,i,j,n,m,t1,t2,t3;
int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值,只要比题目所给任意数大就行
//读入n和m,n表示顶点个数,m表示边的条数
scanf("%d %d",&n,&m);
//初始化
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i==j) e[i][j]=0;
else e[i][j]=inf;
//读入边
for(i=1;i<=m;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
//Floyd-Warshall算法核心语句
for(k=1;k<=n;k++)
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];
//输出最终的结果
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
printf("%10d",e[i][j]);
}
printf("\n");
}
return 0;
}//求多源最短路 //邻接矩阵的方法,输出的是任意两点的距离矩阵
二:spfa算法:是bellman算法的优化写法,用邻接链表和栈来写
求的是给定起点到任意点的带负权最短路
关于邻接链表,可见啊哈磊的讲解http://ahalei.blog.51cto.com/4767671/1391988
适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止
期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。
实现方法:
建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。
判断有无负环:
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
首先建立起始点a到其余各点的
最短路径表格
首先源点a入队,当队列非空时:
1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:
在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点
需要入队,此时,队列中新入队了三个结点b,c,d
队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:
在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要
入队,此时队列中的元素为c,d,e
队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:
在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此
e不用入队了,f要入队,此时队列中的元素为d,e,f
队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:
在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g
队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:
在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e
队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:
在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b
队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:
在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b
队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:
在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了
最终a到g的最短路径为14
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define MAXN 1000
int first[MAXN];//first[i]记录以i为起点的最后一条边的储存位置
int next[MAXN];//next[i]记录与i同起点的上一条边的储存位置
int u[MAXN],v[MAXN],w[MAXN];
int dis[MAXN];//记录起点到i的最长的距离
int book[MAXN];//标记点
int c[MAXN];//统计次数
int n,m;//n为顶点,m为边
int flag;//标记
int spfa(int s,int n){
int i,j,k;
queue<int > q;
memset(dis,0x3f,sizeof(0x3f));
dis[s]=0;
memset(book,0,sizeof(book));
memset(c,0,sizeof(c));
q.push(s);
book[s]=1;
flag=0;
while(!q.empty())
{
int x;
x=q.front();
q.pop();
book[x]=0;//队头元素出队,并且消除标记
for(k=first[k] ; k!=-1 ; k=next[k])//遍历顶点x的邻接表
{
int y=v[k];
if(dis[x]+w[k]<dis[y])
{
dis[y]=dis[x]+w[k]; //松弛
if(!book[y]) //顶点y不在队内
{
book[y]=1; //标记
c[y]++; //统计次数
q.push(y); //入队
if(c[y]>n) //超过入队次数上限,说明有负环
return flag=0;
}
}
}
}
}
int main()
{
int x,y,z;
while(~scanf("%d %d",&n , &m))
{
memset(first,-1,sizeof(first));
for(int i=1 ; i<=m ; i++){
scanf("%d %d %d",&u[i],&v[i],&w[i]);
next[i]=first[u[i]];
first[u[i]]=i;
}
spfa(1,n);
if(flag)
printf("有负权回路\n");
else
{
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
}
}
return 0;
}
/*
bellman算法:
for(int k=1;k<=n-1;k++)//进行n-1次松弛
for(int i=1;i<=m;i++)//枚举每一条边
if(dis[v[i]]>dis[u[i]]+w[i])//尝试松弛每一条边
dis[v[i]]=dis[u[i]]+w[i];
*/
三:dijstra算法:
求给定起点到任意点的最短路(不能有负权)
http://ahalei.blog.51cto.com/4767671/1387799同出自啊哈磊
#include<stdio.h>
#include<string.h>
#define MAXN 1000
#define INF 0x3f3f3f
int e[MAXN][MAXN];//存图
int dis[MAXN];//记录起点到i的最长的距离
int book[MAXN];//标记点
int n,m;//n为顶点,m为边
void dijstra(){
int i,j,k,u,v;
int flag;
memset(book,0,sizeof(book));
for(i=1 ; i<=m ; i++){
dis[i]=e[1][i];
}
book[1]=1;
for(i=1 ; i<=n-1 ; i++)
{
flag=INF;
for(j=1 ; j<=n ; j++)//找到每个过程中距1最近的点
{
if(book[j]==0 && dis[j]<flag)
{
flag=dis[j];
u=j;
}
}
book[u]=1;
for(v=1 ; v<=n ; v++)//进行松弛,判断是否经过转折点使之更小
{
if(e[u][v]<INF)
{
if(dis[v]>dis[u]+e[u][v])
dis[v]=dis[u]+e[u][v];
}
}
}
for(i=1 ; i<=n ; i++)
{
printf("1->%d %d\n",i,dis[i]);//求的是以1为起点的最短路
}
}
int main()
{
int x,y,z;
while(~scanf("%d %d",&n,&m))
{
for(int i=1 ; i<=n ; i++)
for(int j=1 ; j<=n ; j++)
{
if(i == j) e[i][j]=0;
else e[i][j]=INF;
}
for(int i=1 ; i<=m ; i++){
scanf("%d %d %d",&x,&y,&z);
e[x][y]=z;//有向图
}
dijstra();
}
return 0;
}