介绍
全称Bellman-Ford算法,目的是求解有负权边的最短路径问题。
考虑环,根据环中边的边权之和的正负,将环分为零环、正环、负环。其中零环、正环不会影响最短路径的求解,而负环会影响最短路径的求解。
可用BF算法返回一个bool值来判断是否有负环,如果有返回false,否则返回true.
bool BF(int b){
fill(path,path+maxn,INF);
path[b]=0;
//求最短距离
for(int i=0;i<n-1;i++){//比较趟数
for(int j=0;j<n;j++){//遍历每一个顶点相关的邻接边
for(int k=0;k<table[j].size();k++){
int v=table[j][k].v;
int value=table[j][k].value;
if(path[j]+value<path[v]){
//此时path[v]应该是最小距离
path[v]=path[j]+value;
}
}
}
//判断是否有负环:有返回false,无返回true
for(int m=0;m<n;m++){//再次遍历边时
for(int k=0;k<table[m].size();k++){
int v=table[m][k].v;
int value=table[m][k].value;
if(path[m]+value<path[v])
//还能找到有比path[v]更小的距离
return false;//说明有负环存在
}
}
return true;//否则无负环
}
}
设计思想
将求最短路径看作是求以源点为根结点的一棵最短路径树,此时图与起点均确定,因此最短路径树也就确定了,且最短路径树的层数一定不超过顶点个数V,即树中两顶点的比较更新次数不超过V-1轮。
实现
由于用邻接矩阵遍历边时,复杂度会达到O(V的3次方),因此我们使用邻接表去实现
应用
由于求最短路径条数时,BF算法会重复遍历相同的顶点,因此在有多条最短路径数时,最短路径条数的累加会出错。于是我们想到用set容器记录前驱结点,通过遍历去重后的前驱结点进行累加。
set容器的介绍
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
const int maxn=100;
const int INF=1000000000;
struct node{
int v;//邻接顶点
int value;//对应边权
//通过构造函数实现定义同时初始化
node(int a,int b):v(a),value(b){}
};
vector<node> table[maxn];
int n,edge,st,ed,weight[maxn];
int path[maxn],num[maxn],w[maxn];
set<int> pre[maxn];//记录前驱,以便去重
void BF(int b){
fill(path,path+maxn,INF);
memset(num,0,sizeof(num));
memset(w,0,sizeof(w));
path[b]=0;
w[b]=weight[b];
num[b]=1;
for(int i=0;i<n-1;i++){
for(int j=0;j<n;j++){
for(int k=0;k<table[j].size();k++){
//记录
int v=table[j][k].v;
int value=table[j][k].value;
if(path[j]+value<path[v]){
path[v]=path[j]+value;
w[v]=w[j]+weight[v];
num[v]=num[j];//小于覆盖
pre[v].clear();//清空
pre[v].insert(j);//记录最短路径前驱
}else if(path[j]+value==path[v]){
if(w[v]<w[j]+weight[v])
w[v]=w[j]+weight[v];
pre[v].insert(j);//继续记录
num[v]=0;//防止重复计数,清空
//重新累加计数:通过遍历前驱结点实现
for( set<int>::iterator it=pre[v].begin();it!=pre[v].end();it++)
num[v]+=num[*it];//*it=pre[j][k],即k的前驱
}
}
}
}
}
int main(){
int v1,v2,weigh;
cin>>n>>edge>>st>>ed;
for(int i=0;i<n;i++)
cin>>weight[i];
for(int j=0;j<edge;j++){//构建邻接表
cin>>v1>>v2>>weigh;
table[v2].push_back(node(v1,weigh));
table[v1].push_back(node(v2,weigh));
}
BF(st);
cout<<num[ed]<<" "<<w[ed]<<endl;
return 0;
}