对于上面那张图,是可以用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. 无边无状态栏窗口(使用GetWindowLongPtr设置GWL_EXSTYLE)

    通过SetWindowLongPtr来设置窗口样式 var NewStyle: Integer; begin Application.Initialize; Application.MainFormO ...

  2. 低头看—SQL视频

    迷迷糊糊半个月过去了,耿大姐的数据库视频也在一知半解中看完.虽然耿大妈讲的很详细,很细心,但是我还是时不时的犯困(还不如看儿童英语动画片有精神呢)视频看是看完了,但东西不是自己的.这个时候就需要“颗粒 ...

  3. Delphi控件的停靠功能

    Delphi自带的许多控件都有停靠功能,而且操作非常简单,大可不必选用第三方控件.        基本上,要进行Dock操作至少需要两个组件,一个人被附着的Dock Site组件,另一个人附在Dock ...

  4. 浅析Delphi Container库(有开源的DCLX)

    与Java和C++相比,Delphi对容器的支持实在少得可怜.Java有强大的集合框架,C++更有STL,Delphi有什么呢,不就是TList几个小巧的列表类,而TCollection系列的类更多只 ...

  5. Mybatis+Struts2的结合:实现用户插入和查找

    总结一下今天一个成功的小实验:Mybatis+Struts2的结合:实现用户插入和查找.删除和修改如果以后写了,会继续更新. 一 准备工作. 1.新建一个java web项目. 2.在webConte ...

  6. 深刻:截获windows的消息并分析实例(DefWindowProc),以WM_NCHITTEST举例(Windows下每一个鼠标消息都是由 WM_NCHITTEST 消息产生的,这个消息的参数包含了鼠标位置的信息)

    1,回调函数工作机制 回调函数由操作系统自动调用,回调函数的返回值当然也是返回给操作系统了. 2,截获操作系统发出的消息,截获到后,将另外一个消息返回给操作系统,已达到欺骗操作系统的目的. 下面还是以 ...

  7. Windows平台Go调用DLL的坑(居然有这么多没听过的名词)

    最近的项目中,使用了GO来开发一些服务中转程序.业务比较简单,但是有一些业务需要复用原有C++开发的代码.而在WINDOWS,用CGO方式来集成C/C++代码并不是太方便.所以用DLL把C++的代码封 ...

  8. 计算机视觉与模式识别代码合集第二版three

    计算机视觉与模式识别代码合集第二版three     Topic Name Reference code Optical Flow Horn and Schunck's Optical Flow   ...

  9. 跨域GET、POST请求

    跨域GET.POST请求的小结 重点:跨域POST大量数据: JQuery:$.ajax/$.getJSON支持jsonp格式的跨域,但是只支持GET方式,暂不支持POST: CORS:w3c关于跨域 ...

  10. OCA读书笔记(13) - 性能管理

    使用EM监控性能使用自动内存管理(AMM)使用Memory Advisor分配内存查看性能相关动态视图诊断无效的和不可用的对象 创建问题SQLsqlplus / as sysdbaconn scott ...