这个算法是通过一轮一轮的遍历,如果有负环使最短路径更小,那么就更新。
核心代码:
#include <stdio.h>
#include <vector>
using namespace std;
struct Node{
int v,dis;
};
vector<Node>Adj[maxn];
int n;
int d[maxn];
bool Bellman(int s){ //s为原点
fill(d,d+maxn,INF);
d[s] = 0;
for(int i = 0;i <n-1;i++){ //执行n-1轮操作,n为顶点数
for(int u = 0;u <n;u++){ //每轮操作都遍历所有边
for(int j = 0;j <Adj[u].size();j++){
int v = Adj[u][j].v; //邻接边的顶点
int dis = Adj[u][j].dis; //邻接边的边权
if(d[u] + dis<d[v]){ //以u为中介点可以使d[v]更小
d[v] = d[u] + dis; //松弛操作
}
}
}
}
//以下为判断负环的代码
for(int u = 0;u <n;u++){
for(int j = 0;j <Adj[u].size();j++){
int v = Adj[u][j].v;
int dis = Adj[u][j].dis;
if(d[u] + dis < d[v]){
return false;
}
}
}
return true;
}
Bellman算法对于PAT 1003题目的求解:
题目:迪杰斯特拉算法应用——PAT A1003 Emergency
代码:
#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1000000000;
struct Node{
int v,dis;
Node(int _v,int _dis) :v(_v),dis(_dis) {}
};
vector<Node>Adj[maxn];
int n,m,st,ed,weight[maxn];
//d[]记录最短距离,w[]记录最大点权之和,num[]记录最短路径条数
int d[maxn],w[maxn],num[maxn];
set<int>pre[maxn];
void Bellman(int s){
fill(d,d+maxn,INF);
memset(num,0,sizeof(num));
memset(w,0,sizeof(w));
d[s] = 0;
w[s] = weight[s];
num[s] = 1;
//以下为求解数组d部分
for(int i = 0;i <n-1;i++){
for(int u = 0;u <n;u++){
for(int j = 0;j <Adj[u].size();j++){
int v = Adj[u][j].v;
int dis = Adj[u][j].dis;
if(d[u] + dis < d[v]){
d[v] = d[u] + dis;
w[v] = w[u] + weight[v];
num[v] = num[u];
pre[v].clear(); //如果找到最短路径的话,pre先清空,再添加元素
pre[v].insert(u);
}
else if(d[u] + dis == d[v]){
if(w[u] + weight[v] >w[v]){
w[v] = w[u] + weight[v];
}
pre[v].insert(u); //找到另一条同样长度的最短路径,直接添加元素
num[v] = 0; //重新统计num[v]
set<int>::iterator it;
for(it = pre[v].begin();it != pre[v].end();it++){
num[v] += num[*it];
}
}
}
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&st,&ed);
for(int i = 0;i <n;i++){
scanf("%d",&weight[i]);
}
int u,v,wt;
for(int i = 0;i <m;i++){
scanf("%d%d%d",&u,&v,&wt);
Adj[u].push_back(Node(v,wt));
Adj[v].push_back(Node(u,wt));
}
Bellman(st);
printf("%d %d\n",num[ed],w[ed]);
return 0;
}
《算法笔记》P396
优化的Bellman-Ford算法(SPFA),使用队列来优化对于负环的处理(逐渐烧脑= =,大脑已不够用。。。)
伪代码:
//优化的Bellman算法(SPFA)
queue<int>Q;
源s入队;
while(队列非空){
取出队首元素u;
for(u的所有邻接边u->v){
if(d[u] + dis<d[v]){
d[v] = d[u] + dis;
if(v不在当前队列){
v入队;
if(v入队次数>n-1){
说明有可达负环,return;
}
}
}
}
}
实现代码:
PS:书上让自己用SPFA实现下前面10.4.1中的例题,例题的完整实现代码改天复习第二遍的时候再写吧。。。
vector<int>Adj[maxn]; //图的邻接表
int n,d[maxn],num[maxn]; //num数组记录顶点的入队次数
bool inq[maxn]; //顶点是否在队列中
bool SPFA(int s){
memset(inq,false,sizeof(inq));
memset(num,0,sizeof(num));
fill(d,d+maxn,INF);
queue<int>Q;
Q.push(s);
inq[s] = true;
num[s]++;
d[s] = 0;
//主体部分
while(!Q.empty()){
int u = Q.front();
Q.pop();
inq[u] = false;
for(int j = 0;j <Adj[u].size();j++){
int v = Adj[u][j].v;
int dis = Adj[u][j].dis;
if(d[u] + dis<d[v]){
d[v] = d[u] + dis;
if(!inq[v]){
Q.push(v);
inq[v] = true;
num[v]++;
if(num[v]>= n) return false;
}
}
}
}
return true;
}