单源最短路问题,就是对一个边带权图G和其上一点s,求G上每一点到s的权最小路
性质: 1.最短路上不会有环
2.将达不到或者暂时达不到的点的距离习惯记做无穷
3.该问题满足最优子结构,即最短路的子图也为最短路
如何记录:对每个节点v,维护一个值v.pi,表示这个点的前驱节点。求解完成之后使用他构造最短路径树即可、
基本操作:
最短路径估计:对每个点维护一个值v.d,为当前估计的s到他的距离
初始化:将所有v.d置为无穷,v.pi置为null
松弛:对点u,v,若我们已经求出了u的最短路,且u,v邻接,对比u.d+w(u+v)和v.d,若v.d比较大,说明从u到达v比v当前认为的最短路更近,就可以将v.d置为u.d+w(u+v),将v.pi置为u
之后的算法就基于松弛操作进行
bellmanford算法
能够解决带负权图的最短路问题,若图上有负权回路,返回false表示无解
这个算法的思路比较暴力:每次把每条边松弛一次,重复v-1次。每次松弛完所有边后可以保证满足最短路性质的点距离原点的深度多了一层,所以重复v-1次后可以保证最短路已完成。
对于负权回路的判断:完成上面步骤后遍历一遍每条边,若还可以松弛,则说明有负权回路。
优化:如果某次操作中没有松弛,则说明之后也不会再松弛了,在这停止。
以hdu2544为例
//其实这题并没有用到bellmanford判负值的操作
#include <bits/stdc++.h>
using namespace std;
const int maxn=20000;
int vpi[200],vd[200],v,e;//前驱节点,最大值估计
struct edg
{
int vs,ve,w;
}ed[maxn];
void init()
{
memset(vpi,0,sizeof(vpi));
int cs,ce,cw;
for(int i=1;i<=v;i++) vd[i]=100000;
vd[1]=0;
for(int i=1;i<=e/2;i++)
{
cin>>cs>>ce>>cw;
ed[i*2-1].vs=cs;
ed[i*2-1].ve=ce;
ed[i*2-1].w=cw;
ed[i*2].vs=ce;
ed[i*2].ve=cs;
ed[i*2].w=cw;
}
}
bool bellmanford()
{
for(int i=1;i<=v;i++)
{
bool flag=true;
for(int j=1;j<=e;j++)
{
if(vd[ed[j].ve]>vd[ed[j].vs]+ed[j].w)
{
vd[ed[j].ve]=vd[ed[j].vs]+ed[j].w;
vpi[ed[j].ve]=ed[j].vs;
flag=false;
}
}
if(flag)break;
}
for(int i=1;i<=e;i++)
{
if(vd[ed[i].ve]>vd[ed[i].vs]+ed[i].w)
return false;
}
return true;
}
int main()
{
while(cin>>v>>e,v!=0)
{
e*=2;
init();
bellmanford();
cout<<vd[v]<<endl;
}
return 0;
}