对于上面那张图,是可以用dij算法求解出正确答案,但那只是巧合而已。

我们再看看下面这张图。

dist[4] 是不会被正确计算的。 因为dij算法认为从队列出来的点,(假设为u)肯定是已经求出最短路的点,标记点u。并用点u更新其它点。

所以如果存在负权使得这个点的权值更小,那么会更新dist[u], 但是因为这个点已经被标记了,所以dij算法不会用这个点来更新其它点,所以就导致了算法的错误。

归结原因,dij算法在存在负权的时候,过早得确立某个点最短路,以至于如果这个点不是最短路,就会导致错误。

如下面的算法所示

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
#pragma warning(disable:4996)
typedef long long LL;
const int INF = <<; const int N = + ;
struct Edge
{
int to,dist;
bool operator<(const Edge&rhs)const
{
return dist > rhs.dist;
}
};
vector<Edge> g[N];
int dist[N];
bool vis[N];
void dij(int start, int n)
{
memset(vis,,sizeof(vis));
for(int i=; i<=n; ++i)
dist[i] = INF;
priority_queue<Edge> q;
Edge tmp,cur;
dist[start] = cur.dist = ;
cur.to = start;
q.push(cur);
while(!q.empty())
{
cur = q.top(); q.pop();
int u = cur.to; /*
如果u被用来更新过其它点,那么即使存在负权使得dist[u]变小,
那么dij算法也不会再用u来更新其它点,这就是dij不能处理负权回路的原因
*/
if(vis[u]) continue;
vis[u] = true;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i].to;
if(dist[v] > dist[u] + g[u][i].dist)
{
tmp.dist = dist[v] = dist[u] + g[u][i].dist;
tmp.to = v;
q.push(tmp);
}
}
} } int main()
{
int n,m,a,b,c,i;
Edge tmp;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=; i<m; ++i)
{
scanf("%d%d%d",&a,&b,&c);
tmp.to = b;
tmp.dist= c;
g[a].push_back(tmp);
}
dij(,n);
for(i=; i<=n; ++i)
printf("%d ",dist[i]);
puts("");
}
return ;
}

4 4
1 2 3
1 3 2
2 3 -2
3 4 2
dist[0->4] = 0 3 1 4; wrong

那么我们可以对上面的算法进行改进,上面算法的问题在于,一个点如果被标记以后,那么这个点是不会用来更新其它点的,哪怕到这个点的最短路径减小了,也不会用来更新其它点。

所以新的改进是我们允许一个点出队列多次,只要这个点对最短路的更新有贡献。

只要dist[u]>=cur.dist   ,  那么我们就认为点u可能对最短路的更新有贡献,所以让点u去更新最短路。

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
#pragma warning(disable:4996)
typedef long long LL;
const int INF = <<;
/*
3 3
1 2 3
1 3 2
2 3 -2 4 4
1 2 3
1 3 2
2 3 -2
3 4 2
dist[0->4] = 0 3 1 3; correct */
const int N = + ;
struct Edge
{
int to,dist;
bool operator<(const Edge&rhs)const
{
return dist > rhs.dist;
}
};
vector<Edge> g[N];
int dist[N];
//允许一个点入队列多次,是spfa算法, 可以看做是dij的变种或者bellman的变种
void spfa(int start, int n)
{
for(int i=; i<=n; ++i)
dist[i] = INF;
priority_queue<Edge> q;
Edge tmp,cur;
dist[start] = cur.dist = ;
cur.to = start;
q.push(cur);
while(!q.empty())
{
cur = q.top(); q.pop();
int u = cur.to;
if(dist[u]<cur.dist) continue; //这里就是允许点u用来多次更新其它点的关键
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i].to;
if(dist[v] > dist[u] + g[u][i].dist)
{
tmp.dist = dist[v] = dist[u] + g[u][i].dist;
tmp.to = v;
q.push(tmp);
}
}
} } int main()
{
int n,m,a,b,c,i;
Edge tmp;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=; i<m; ++i)
{
scanf("%d%d%d",&a,&b,&c);
tmp.to = b;
tmp.dist= c;
g[a].push_back(tmp);
}
dij(,n);
for(i=; i<=n; ++i)
printf("%d ",dist[i]);
puts("");
}
return ;
}

4 4
1 2 3
1 3 2
2 3 -2
3 4 2
dist[0->4] = 0 3 1 3; correct

dij算法为什么不能处理负权,以及dij算法变种的更多相关文章

  1. 图之单源Dijkstra算法、带负权值最短路径算法

    1.图类基本组成 存储在邻接表中的基本项 /** * Represents an edge in the graph * */ class Edge implements Comparable< ...

  2. [算法] dijkstra单源无负权最小路径算法

    #include <stdio.h>#include <stdlib.h>#include <string.h> #define INF 1000000#defin ...

  3. Dijkstra 算法,用于对有权图进行搜索,找出图中两点的最短距离

    Dijkstra 算法,用于对有权图进行搜索,找出图中两点的最短距离,既不是DFS搜索,也不是BFS搜索. 把Dijkstra 算法应用于无权图,或者所有边的权都相等的图,Dijkstra 算法等同于 ...

  4. Floyd算法-傻子也能看懂的弗洛伊德算法(转)

                暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程.          ...

  5. python数据结构与算法——图的最短路径(Bellman-Ford算法)解决负权边

    # Bellman-Ford核心算法 # 对于一个包含n个顶点,m条边的图, 计算源点到任意点的最短距离 # 循环n-1轮,每轮对m条边进行一次松弛操作 # 定理: # 在一个含有n个顶点的图中,任意 ...

  6. 单源最短路:Dijkstra算法 及 关于负权的讨论

    描述: 对于图(有向无向都适用),求某一点到其他任一点的最短路径(不能有负权边). 操作: 1. 初始化: 一个节点大小的数组dist[n] 源点的距离初始化为0,与源点直接相连的初始化为其权重,其他 ...

  7. Bellman-Ford算法——解决负权边

    Dijkstra算法虽然好,但是它不能解决带有负权边(边的权值为负数)的图. 接下来学习一种无论在思想上还是在代码实现上都可以称为完美的最短路径算法:Bellman-Ford算法. Bellman-F ...

  8. 非负权值有向图上的单源最短路径算法之Dijkstra算法

    问题的提法是:给定一个没有负权值的有向图和其中一个点src作为源点(source),求从点src到其余个点的最短路径及路径长度.求解该问题的算法一般为Dijkstra算法. 假设图顶点个数为n,则针对 ...

  9. Bellman-ford算法与SPFA算法思想详解及判负权环(负权回路)

    我们先看一下负权环为什么这么特殊:在一个图中,只要一个多边结构不是负权环,那么重复经过此结构时就会导致代价不断增大.在多边结构中唯有负权环会导致重复经过时代价不断减小,故在一些最短路径算法中可能会凭借 ...

随机推荐

  1. (step4.3.9)hdu 1584(蜘蛛牌——DFS)

    题目大意:本体是中文题,可以直接在OJ上看 /* * 1584_2.cpp * * Created on: 2013年8月22日 * Author: Administrator */ #include ...

  2. 管道实现进程间通讯 、WaitNamedPipe

    一.管道实现进程间通讯 主要的理论知识 1.什么是管道以及分类 管道是两个头的东西,每一个头各连接一个进程或者同一个进程的不同代码,依照管道的类别分有两种管道,匿名的和命名的:依照管道的传输方向分也能 ...

  3. callback用法简介

    源地址:https://argcv.com/articles/2669.c callback,函数的回调,从ANSI C开始,一直被广为使用.无论是windows API的所谓消息机制,动态链接库的调 ...

  4. Android installed app, never used, cannot receiver BroadcastReceiver

    官方文档是这么写的:(http://developer.android.com/about/versions/android-3.1.html#launchcontrols) Launch contr ...

  5. iOS 开发百问(6)

    61.警告"addexplicit braces to avoid dangling else" 所谓"危急的else"是相似这种代码: if(a== 10) ...

  6. Java NIO 完全学习笔记(转)

    本篇博客依照 Java NIO Tutorial翻译,算是学习 Java NIO 的一个读书笔记.建议大家可以去阅读原文,相信你肯定会受益良多. 1. Java NIO Tutorial Java N ...

  7. HDU 3571 N-dimensional Sphere(高斯消元 数论题)

    这道题算是比较综合的了,要用到扩展欧几里得,乘法二分,高斯消元. 看了题解才做出来orz 基本思路是这样,建一个n*(n-1)的行列式,然后高斯消元. 关键就是在建行列式时会暴long long,所以 ...

  8. 怎么制作U盘系统/PE启动盘

    原地址:http://jingyan.baidu.com/article/3c48dd3453d05de10be358fb.html 不用光驱就能启动系统,做一个U盘启动系统就搞定,下面我给大家演示如 ...

  9. Linux ssh密钥自动登录(转)

    在开发中,经常需要从一台主机ssh登陆到另一台主机去,每次都需要输一次login/Password,很繁琐.使用密钥登陆就可以不用输入用户名和密码了 实现从主机A免密码登陆到主机B,需要以下几个步骤: ...

  10. java 获取当期时间之前几小时的时间

    Calendar expireDate = Calendar.getInstance(); expireDate.set(Calendar.HOUR_OF_DAY, expireDate.get(Ca ...