http://acm.pku.edu.cn/JudgeOnline/problem?id=1125
poj 1125这道题,题意为,找到所有可行路径(即从源点可到达所有其他的点,注意是有向图,所以必须要以)中,相距最远两点的距离最小。解法当然就是求最短路,分别用dijkstra,bellman_ford,spfa进行求解,但可惜这题数据实在太弱,时间都是0ms
简要概括三种最短路算法,也算一个学习小结
dijkstra 单源最短路径
不能存在负权边,复杂度为O(n*n),求源点s到任意点的最短距离,d[i]表示源点s到i的最短路
初始:d[s]=0,d[i]=无穷大(i!=s)
bellman_ford 单源最短路径
可以存在负权边,但不能出现负权回路。可以找到图中是否存在负权回路。复杂度为O(VE)
求源点s到任意点的最短距离,d[i] 表示源点s到i的最短距离
判断负权回路:在v-1次松弛后,如果还能进行松弛,则存在负权回路
初始:d[s]=0,d[i]=无穷大(i!=s)
spfa 最短路加速算法
是bellman_ford的一种队列实现,减少了很多冗余的计算。初始时将源加入队列。每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。直到队列为空时算法结束。复杂度为O(KE),K平均来说不超过2
判断负权回路:若某一个节点i入队的次数超过v次,则存在负权回路(v为节点的个数)
初始:d[s]=0,d[i]=无穷大(i!=s)
v[s]=true,v[i]=false(i!=s)
q.push(s)
三个算法的源代码实现如下:
/*dijkstra*/
const int INF=(1<<30)-1;
const int N=110;
int n;
int path[N][N];
int start;
int ans;
void dijkstra(int s)
{
int d[N];
bool v[N];
for(int i=1;i<=n;i++)d[i]=INF;
d[s]=0;
memset(v,false,sizeof(v));
for(int i=1;i<=n;i++)
{
int Min=INF;
int mark=-1;
for(int j=1;j<=n;j++)
if(!v[j]&&d[j]<Min){Min=d[j];mark=j;}
if(mark==-1)break;//每次循环都会加入一个新的节点,当某一次找不到新节点时,则不存在s到所有点的最短路
v[mark]=true;
for(int j=1;j<=n;j++)
if(!v[j]&&d[j]>d[mark]+path[mark][j])d[j]=d[mark]+path[mark][j];
}
}
/*bellman_ford*/
const int INF=(1<<30)-1;
const double EPS=1e-8;
const int N=110;
struct E
{
int u,v,path;
}e[N*N];
int ans;
int k;
int n;
int num;
void bellman_ford(int s)
{
int d[N];
for(int i=1;i<=n;i++)d[i]=INF;
d[s]=0;
for(int i=1;i<=n-1;i++)
{
bool flag=true;
for(int j=0;j<num;j++)//每次都用所有的边(无向图则算两条边)进行松弛
if(d[e[j].v]>d[e[j].u]+e[j].path)
{flag=false;d[e[j].v]=d[e[j].u]+e[j].path;}
if(flag)break ; //提前结束循环
}
for(int j=0;j<num;j++)//判断负权回路
if(d[e[j].v]>d[e[j].u]+e[j].path)
存在负权回路;
}
/*spfa*/
const int INF=(1<<30)-1;
const double EPS=1e-8;
const int N=110;
int path[N][N];
int ans;
int k;
int n;
void spfa(int s)
{
int d[N];
bool v[N];
for(int i=1;i<=n;i++)d[i]=INF;
d[s]=0;
memset(v,false,sizeof(v));
v[s]=true;
queue<int> q;//队列
q.push(s);
while(!q.empty())//队列为空,松弛结束
{
int t=q.front();
q.pop();
v[t]=false;
for(int i=1;i<=n;i++)//用队首节点t对与它相邻的边,进行松弛
{
if(d[i]>d[t]+path[t][i])//如果松弛成功,且与t相邻的节点i不在队列中,则i入队
{
d[i]=d[t]+path[t][i];
if(!v[i]){q.push(i);v[i]=true;}
}
}
}
}