BF
Dijkstra算法可以很好的解决无负权图,但如果出现了负权边,便会失效。
首先,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。,如果把源点S作为一棵树的根结点,把其他结点按照最短路径的结点顺序连接,就会生成一棵最短路径树。
主要思路如下(伪代码)复杂度为(V*E):
for(i=0;i<n-1;i++)//n为顶点数
{
for(each edge u->v)//每轮操作遍历所有边
{
if(d[u]+length[u->v]<d[v])//以u为中介点可以使的d[v]更小
{
d[v]=d[u]+length[u->v];//松弛操作
}
}
}
邻接表的代码:
struct node
{
int v,dis; //v为邻接边的目标定点,dis为邻接边的边权
};
vector<node> aj[maxx];
int n;
int d[maxx]; //起点到达各点的最短路径长度
bool bellman(int s) //s为源点
{
fill(d,d+maxx,INF);
d[s]=0;
for(int i=0;i<n-1;i++)
for(int u=0;u<n;u++)
for(int j=0;j<aj[u].size();u++)
{
int v=aj[u][j].v;//邻接边的顶点
int dis=aj[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<aj[u].size();j++)
{
int v=aj[u][j].v;
int dis=aj[u][j].dis;
if(d[u]+dis<d[v]) //如果仍然可以被松弛
{
return false; //有负环
}
}
return true;
}
SPFA
Bellman-Ford算法时间复杂度过高,优化后被称为SPFA算法。
代码思路如下:
struct node
{
int v,dis; //v为邻接边的目标定点,dis为邻接边的边权
};
vector<node> aj[maxx];
int n;
int d[maxx]; //起点到达各点的最短路径长度
bool inq[maxx]; //顶点是否在队列中
bool SPFA(int s)
{
memset(inq,false,sizeof(inq));
memset(num,0,sizeof(num));
fill(d,d+maxx,INF);
queue<int> q;
q.push(s); //源点入队
inq[s]=true; //原点已入队
num[s]++; //入队次数加1
d[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
inq[u]=false;
for(int j=0;j<aj[u].size();j++)
{
int v=aj[u][j].v;
int dis=aj[u][j].dis;
if(d[u]+dis<d[v])
{
d[v]=d[u]+dis;
if(!inq[v])//如果v不在队列中
{
q.push(v);
inq[v]=true;
num[v]++;
if(num[v]>=n)//超过了n
return false; //有可达负环
}
}
}
}
return true;
}