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算法思想详解及判负权环(负权回路)
我们先看一下负权环为什么这么特殊:在一个图中,只要一个多边结构不是负权环,那么重复经过此结构时就会导致代价不断增大.在多边结构中唯有负权环会导致重复经过时代价不断减小,故在一些最短路径算法中可能会凭借 ...
随机推荐
- 关于sizeof的笔试面试题具体解释
原创Blog,转载请注明处处 http://blog.csdn.net/hello_hwc 注意:sizeof是编译期计算出结果的,这一点对后面的理解非常重要 一.关于结构体 先看下代码 #inclu ...
- setenv 和 set
setenv 和 set 是在csh系列的命令,当然bash中也有set,还是有出入的. set 是对当前进程有效,不会传递给子进程 setenv 不仅对当前进程有效,也会传递给子进程. 语法 ...
- C#反射Assembly 具体说明
1.对C#反射机制的理解 2.概念理解后,必须找到方法去完毕,给出管理的主要语法 3.终于给出有用的样例,反射出来dll中的方法 反射是一个程序集发现及执行的过程,通过反射能够得到*.exe或*.dl ...
- POJ 1742 hdu 2844 Coins
题目链接:http://poj.org/problem?id=1742 http://acm.hdu.edu.cn/showproblem.php?pid=2844 题目分类:动态规划 代码: #in ...
- Delphi数据类型转换(有几个字符串函数没见过,比如StringToWideChar和WideCharToString)
DateTimeToFileDate 函数 将DELPHI的日期格式转换为DOS的日期格式 DateTimeT ...
- 当Scheduler拿不到url的 时候,不能立即退出
在webmagic的多线程抓取中有一个比较麻烦的问题:当Scheduler拿不到url的 时候,不能立即退出,需要等到没抓完的线程都运行完毕,没有新url产生时,才能退出.之前使用Thread.sle ...
- 算法起步之Prim算法
原文:算法起步之Prim算法 prim算法是另一种最小生成树算法.他的安全边选择策略跟kruskal略微不同,这点我们可以通过一张图先来了解一下. prim算法的安全边是从与当前生成树相连接的边中选择 ...
- Android架构设计和软硬整合完整训练
Android架构设计和软硬整合完整训练 Android架构设计和软硬整合完整训练:HAL&Framework&Native Service&Android Service&a ...
- 在Mybatis中使用注解@多个參数查询
@Select("SELECT * FROM wc_homework WHERE organization_id=#{classId} ORDER BY createtime DESC LI ...
- POJ-1324-Holedox Moving(BFS)
Description During winter, the most hungry and severe time, Holedox sleeps in its lair. When spring ...