dij算法为什么不能处理负权,以及dij算法变种

对于上面那张图,是可以用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算法变种的更多相关文章
- 图之单源Dijkstra算法、带负权值最短路径算法
1.图类基本组成 存储在邻接表中的基本项 /** * Represents an edge in the graph * */ class Edge implements Comparable< ...
- [算法] dijkstra单源无负权最小路径算法
#include <stdio.h>#include <stdlib.h>#include <string.h> #define INF 1000000#defin ...
- Dijkstra 算法,用于对有权图进行搜索,找出图中两点的最短距离
Dijkstra 算法,用于对有权图进行搜索,找出图中两点的最短距离,既不是DFS搜索,也不是BFS搜索. 把Dijkstra 算法应用于无权图,或者所有边的权都相等的图,Dijkstra 算法等同于 ...
- Floyd算法-傻子也能看懂的弗洛伊德算法(转)
暑假,小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间则没有,如下图.为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程. ...
- python数据结构与算法——图的最短路径(Bellman-Ford算法)解决负权边
# Bellman-Ford核心算法 # 对于一个包含n个顶点,m条边的图, 计算源点到任意点的最短距离 # 循环n-1轮,每轮对m条边进行一次松弛操作 # 定理: # 在一个含有n个顶点的图中,任意 ...
- 单源最短路:Dijkstra算法 及 关于负权的讨论
描述: 对于图(有向无向都适用),求某一点到其他任一点的最短路径(不能有负权边). 操作: 1. 初始化: 一个节点大小的数组dist[n] 源点的距离初始化为0,与源点直接相连的初始化为其权重,其他 ...
- Bellman-Ford算法——解决负权边
Dijkstra算法虽然好,但是它不能解决带有负权边(边的权值为负数)的图. 接下来学习一种无论在思想上还是在代码实现上都可以称为完美的最短路径算法:Bellman-Ford算法. Bellman-F ...
- 非负权值有向图上的单源最短路径算法之Dijkstra算法
问题的提法是:给定一个没有负权值的有向图和其中一个点src作为源点(source),求从点src到其余个点的最短路径及路径长度.求解该问题的算法一般为Dijkstra算法. 假设图顶点个数为n,则针对 ...
- Bellman-ford算法与SPFA算法思想详解及判负权环(负权回路)
我们先看一下负权环为什么这么特殊:在一个图中,只要一个多边结构不是负权环,那么重复经过此结构时就会导致代价不断增大.在多边结构中唯有负权环会导致重复经过时代价不断减小,故在一些最短路径算法中可能会凭借 ...
随机推荐
- 从源代码角度分析ViewStub 疑问与原理
一.提出疑问 ViewStub比較简单.之前文章都提及到<Android 性能优化 三 布局优化ViewStub标签的使用>.可是在使用过程中有一个疑惑,究竟是ViewStub上设 ...
- java.lang.NoSuchMethodError: org.springframework.beans.factory.annotation.InjectionMetadata.<init>(L
关于错误: java.lang.NoSuchMethodError: org.springframework.beans.factory.annotation.InjectionMetadata.&l ...
- 爬虫总结_python
import sqlite3 Python 的一个非常大的优点是很容易写很容易跑起来,缺点就是很多不那么著名的(甚至一些著名的)程序和库都不像 C 和 C++ 那边那样专业.可靠(当然这也有动态类型 ...
- drupal THEME主要文件
**.info 文件** .info 文件是一个必需的文件:Drupal 必须包括它,才干看到主题. .info 文件告诉 Drupal 主题的内部名称.比如,假设这个文件的名称是 ibmtheme. ...
- moodle中文API之表单API
Form API 表单API 文件夹 1.概述 2.亮点 3.使用方法 4.表单元素 4.1 基本表单元素 4.2 定制表单元素 5.经常使用函数 5.1 add_action_buttons($c ...
- <Win32_17>集音频和视频播放功能于一身的简易播放器
前段时间,在学习中科院杨老师的教学视频时,他说了一句话: "我很反对百八十行的教学程序,要来就来一个完整的程序" 对此,我很是赞同.所谓真刀真枪的做了,你才会发现其中的奥秘——然而 ...
- android用于打开各种文件的intent
import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.n ...
- 为什么国外程序员爱用苹果Mac电脑?(转)
Mac 在国外很受欢迎,尤其是在 设计/web开发/IT 人员圈子里.普通用户喜欢 Mac 可以理解,毕竟 Mac 设计美观,简单好用,没有病毒.那么为什么专业人士也对 Mac 情有独钟呢?从个人使用 ...
- 从零開始学习制作H5应用——V5.0:懊悔机制,整理文件夹,压缩,模板化
经过前面四个版本号的迭代.我们已经制作了一个从视觉和听觉上都非常舒服的H5微场景应用,没有看过的请戳以下: V1.0--简单页面滑动切换 V2.0--多页切换.透明过渡及交互指示 V3.0--增加lo ...
- MD5 概念与用途
MD5概念: MD5这是message-digest algorithm 5(信息-摘要算法)缩写.用于加密和解密技术上,它能够说是文件的"数字指纹".不论什么一个文件,不管是可运 ...