解决这类问题的核心思想就是通过中间点的松弛来使路径变短(这好像和Floyd以及单元最短路径有些相似),不过此种算法是遍历n-1遍每一条边(可能不用这么多,为保险起见最多是这样不过这也是可以优化的)
还是先看一下核心代码:
for(int k=1; k<=n-1; k++)//遍历n-1次
//遍历n-1遍是因为在n个顶点的路径中任意两点之间的最短路径最多只
//可能包含n-1条边(最多只可能通过这么多的路径是两点之间的距离
//变为最短)
for(int i=1; i<=m; i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])
//核心判断看dis[v[i]即1号顶点到v[i]之间的距离是否可
//以通过1号点到u[i]再到v[i]来使路径变短
dis[v[i]]=dis[u[i]]+w[i];
}
下面还是来举个例子帮助理解:
我们给出一组数据
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
dis数组初始状态:(1表示无穷大)
1 2 3 4 5
dis 0 1 1 1 1
下面来进行第一轮松弛:
dis[3]=无穷大dis[2]也是无穷大(注意此时dis[2]还不等与-3)+2因此不能松弛,下一组数dis[2]=无穷大但是dis[1]±3=-3满足松弛条件,dis[2]变为-3,下一组数dis[5]为无穷大通过dis[1]+w[i]=5满足条件变为5,后面的也是这一种做法,自行演示;
第一次后数组变为:
1 2 3 4 5
dis 0 -3 1 1 5
按照同样方法对其进行一条边一条边的遍历,(还是从2 3 2
)那一条开始
第二次数组变化:
1 2 3 4 5
dis 0 -3 -1 2 5
第三次变化:
1 2 3 4 5
dis 0 -3 -1 2 4
第四次变化:
1 2 3 4 5
dis 0 -3 -1 2 4
最后一组即为1号点到各条边之间的最小距离
AC代码为
#include<stdio.h>
int inf=99999999;
int dis[20050];
int u[20050],v[20050],w[20050];
int main()
{
int n,m;
while(scanf("%d %d", &n,&m)!=EOF)
{
for(int i=1; i<=m; i++)
scanf("%d %d %d", &u[i],&v[i],&w[i]);
for(int i=1; i<=n; i++)
dis[i]=inf;
dis[1]=0;
for(int k=1; k<=n-1; k++)
for(int i=1; i<=m; i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
}
for(int i=1; i<=n; i++)
printf("%d ", dis[i]);
printf("\n");
}
return 0;
}
负权回路(回路权值之和为负)的判断
在核心语句后面加上一条判断就行
for(int k=1; k<=n-1; k++)
for(int i=1; i<=m; i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
}
flag=0;
for(int i=1; i<=m; i++)
{
if(dis[v[i]]>dis[u[i]]+w[i])
flag=1;
}
if(flag==1)
printf("有负权回路\n");
其他的代码和上面的一样把他套用进去就行,在这就不写出来了;