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算法思想详解及判负权环(负权回路)
我们先看一下负权环为什么这么特殊:在一个图中,只要一个多边结构不是负权环,那么重复经过此结构时就会导致代价不断增大.在多边结构中唯有负权环会导致重复经过时代价不断减小,故在一些最短路径算法中可能会凭借 ...
随机推荐
- 使用Iterator遍历Sheet(POI)验证及解释结果有序性
test.xlsx: Code: package poi; import static org.junit.Assert.*; import java.io.IOException; import j ...
- 存储的几个LUN问题
存储的几个LUN问题 . ---整理自EMC论坛 1. Linux中如何识别LUN?(AIX是否也差不多) 当创建好LUN并建好storage group后,主机(linux)可以直接用fdisk - ...
- 5350.support
3G6200N3G6200NL3G300MAIR3GIIALL02393GALL0256NALL5002ALL5003ARGUS_ATP52B,ASL26555AWM002EVBAWAPN2403BC ...
- Spring Security Source Code -- 验证标准流程
除了初始阶段: 主干验证流程链: MyInvocationSecurityMetadataSource.getAttributes(Object) line: 43 MyFilterSecur ...
- java中synchronized的使用方法与具体解释
Java语言的keyword.当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多仅仅有一个线程运行该段代码. 一.当两个并发线程訪问同一个对象object中的这个synchronized ...
- ExtJs4 笔记(8) Ext.slider 滚轴控件、 Ext.ProgressBar 进度条控件、 Ext.Editor 编辑控件
本篇要登场的有三个控件,分别是滚轴控件.进度条控件和编辑控件. 一.滚轴控件 Ext.slider 1.滚轴控件的定义 下面我们定义三个具有代表意义滚轴控件,分别展示滚轴横向.纵向,以及单值.多值选择 ...
- 14.4.3.6 Fine-tuning InnoDB Buffer Pool Flushing 微调 InnoDB Buffer Pool 刷新:
14.4.3.6 Fine-tuning InnoDB Buffer Pool Flushing 微调 InnoDB Buffer Pool 刷新: innodb_flush_neighbors an ...
- Oracle heap 表的主键 dump 分析
1. 创建heap 表: create table t1 (id char(10) primary key,a1 char(10),a2 char(10),a3 char(10)); SQL> ...
- JavaFX游戏开发效率浅谈
声明: 本博客文章原创类别的均为个人原创,版权所有.转载请注明出处: http://blog.csdn.net/ml3947,另外本人的个人博客:http://www.wjfxgame.com. ...
- android--手机桌面添加网址链接图标(解决方式)
这样的做法最普遍最简单: 1.新建一个android空项目: 2.在drawable文件夹下加入图标文件,如icon.png:在values文件夹下的strings.xml文件里添加名称.如websi ...