关于最短路径问题,最近学了四种方法——bellman算法、邻接表法、dijkstra算法和floyd-warshall算法。
这当中最简单的为bellman算法,通过定义一个边的结构体,存储边的起点、终点和路径长度,然后通过一个while(1)死循环不断地访问每一条边,更新源点到各点的最短距离,直到没有更新时结束。这时便得到了从源点到其他点的最短距离。附上代码一段:
#include<iostream>
#define INF 100000000
using namespace std;
struct eg
{
int s,t;
int c;
};//边的结构体,存储元素为起点s,终点t,路径s到t之间的长度
eg Eg[100];
int dis[100];
void bellman(int s,int E)
{
fill(dis,dis+100,INF);//初始化所有最短距离为INF;
dis[s]=0;//但源点的最短距离为0,自己到自己
while(1)
{
bool update= false;
for(int i=0;i<E;i++)
{
eg e=Eg[i];
if(dis[e.s]!=INF && dis[e.t]>dis[e.s]+e.c)//更新
{
dis[e.t]=dis[e.s]+e.c;
update = true;
}
}
if(!update)//如果遍历所有的边均不再有更新,则跳出循环
break;
}
}
int main()
{
int E;//定义边的变量
cin >> E;
for(int i=0;i<E;i++)//直接存储边
{
cin >> Eg[i].s >> Eg[i].t >> Eg[i].c;
}
int s1;
cin >> s1;//定义一个源点
bellman(s1,E);
int t;
cin >> t;
cout << dis[t] << endl;
return 0;
}
综上代码我们可以分析,bellman算法的时间复杂度为o(v*e),while循环最多执行v-1次;bellman算法还存在一个问题在于负圈,如果图中存在源点s可达的负圈(图中存在的环并且这个环里面有负边(边的值为负值)),当while循环更新时,走到这个负圈,dis【e.t】>dis[e.s]+e.c则是恒成立,每次的while循环中都会有更新,这样再用刚刚的那个方法的话就会形成死循环,所以,用bellman算法得保证图中不存在源点s可达的负圈。附上查找负圈的代码:
bool find_negative_loop()
{
memset(dis,0,sizeof(dis));//注意和fill的写法不同(fill(dis,dis+e,INF))
for(int i=1;i<=v;i++)
for(int j=0;j<e;j++)
{
eg e=Eg[j];
if(dis[e.t]>dis[e.s]+e.c)
{
dis[e.t]=dis[e.s]+e.c;
if(i==v) return true;
}
}
}