最短路径中
不能包含负权回路
,有负权回路的边可能不存在最短路
。因为每次经过负权回路,路径的权值会减少。有些图结构中会存在负权边,用于表达通过某条途径可以降低总消耗,在有向图中,负权边不一定会形成负权回路,所以在一些计算最短路径算法中,负权边也可以计算出最短路径;在无向图中,负权边就意味着负权回路,所以无向图中不能存在负权边
。
作者:zhipingChen
链接:https://www.jianshu.com/p/b876fe9b2338
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
松弛函数
对边集合
E
E
E 中任意边,以
w
(
u
,
v
)
w(u,v)
w(u,v)表示顶点
u
u
u 出发到顶点
v
v
v 的边的权值,以
d
[
v
]
d[v]
d[v] 表示当前从起点
s
s
s 到顶点
v
v
v 的路径权值
若存在边
w
(
u
,
v
)
w(u,v)
w(u,v),使得:
d
[
v
]
>
d
[
u
]
+
w
(
u
,
v
)
d[v]>d[u]+w(u,v)
d[v]>d[u]+w(u,v)
则更新
d
[
v
]
d[v]
d[v] 值:
d
[
v
]
=
d
[
u
]
+
w
(
u
,
v
)
d[v]=d[u]+w(u,v)
d[v]=d[u]+w(u,v)
所以松弛函数的作用,就是判断是否经过某个顶点,或者说经过某条边,可以缩短起点到终点的路径权值
为什么将缩短距离的操作称之为“松弛”,不妨理解为,选择某种方式后,到达目的的总代价降低了。什么名字无关紧要,不必纠结。
作者:zhipingChen
链接:https://www.jianshu.com/p/b876fe9b2338
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
B e l l m a n _ f o r d Bellman\_ford Bellman_ford算法
可以用来求负权边最短路
找一个图是否有负环,但一般不用 B e l l m a n _ f o r d Bellman\_ford Bellman_ford来做,一般用 s p f a spfa spfa来做
有边数限制的只能用 B e l l m a n _ f o r d Bellman\_ford Bellman_ford来做,其他都不行
模板题:AcWing 853.有边数限制的最短路
步骤如下:
1、老规矩,初始化距离数组:
m
e
m
s
e
t
(
d
i
s
t
,
0
x
3
f
,
s
i
z
e
o
f
(
d
i
s
t
)
)
memset(dist, 0x3f, sizeof(dist))
memset(dist,0x3f,sizeof(dist))
d
i
s
t
[
1
]
=
0
;
dist[1]=0;
dist[1]=0;
2、 f o r ( i = 1 ; i < = k ; + + i ) for(i=1;i<=k;++i) for(i=1;i<=k;++i) 这个含义是迭代k次,就能求出不超过 k k k 条边的最短距离
3、
f
o
r
所
有
边
a
,
b
,
w
for 所有边 a,b,w
for所有边a,b,w,如果两个点之间不存在边就不用更新,我们只更新存在两个点之间存在边的边
d
i
s
t
[
b
]
=
m
i
n
(
d
i
s
t
[
b
]
,
d
i
s
t
[
a
]
+
w
)
dist[b]=min(dist[b],dist[a]+w)
dist[b]=min(dist[b],dist[a]+w)PS:这一步称为松弛操作
4、
i
f
(
d
i
s
t
[
n
]
>
0
x
3
f
3
f
3
f
3
f
/
2
)
if(dist[n]>0x3f3f3f3f/2)
if(dist[n]>0x3f3f3f3f/2),说明不超过
k
k
k 条边,到不了
n
n
n 这个点
理由:假如
m
m
m 到
n
n
n 存在一个负权
−
2
-2
−2,那么更新的时候
d
i
s
t
[
n
]
=
m
i
n
(
d
i
s
t
[
n
]
,
d
i
s
t
[
m
]
+
(
−
2
)
)
dist[n]=min(dist[n],dist[m]+(-2))
dist[n]=min(dist[n],dist[m]+(−2))
这个时候的
d
i
s
t
[
n
]
dist[n]
dist[n] 就不是
0
x
3
f
3
f
3
f
3
f
0x3f3f3f3f
0x3f3f3f3f 了,而是比
0
x
3
f
3
f
3
f
3
f
0x3f3f3f3f
0x3f3f3f3f还小的数,但不会小太多,这道题最多小
10000
∗
500
=
5000000
10000*500=5000000
10000∗500=5000000,五百万这么多。所以除以
2
2
2 就是了
注意在更新距离的时候我们一定要备份一个距离数组,防止出现串联用多次
这道题我们用结构体来存边,AC代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
struct Edge
{
int a,b,c;
}edge[MAXN];
int dist[MAXN],backup[MAXN],n,m,k,x,y,z;
int bellman_ford()
{
memset(dist,0x3f,sizeof(dist));//初始化
dist[1]=0;
for(int i=1;i<=k;++i)
{
memcpy(backup,dist,sizeof(dist));//记得备份啊
for(int j=1;j<=m;++j)
dist[edge[j].b]=min(dist[edge[j].b],backup[edge[j].a]+edge[j].c);
}
if(dist[n]>0x3f3f3f3f/2)
printf("impossible");
else
printf("%d",dist[n]);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&x,&y,&z);
edge[i]={x,y,z};
}
bellman_ford();
return 0;
}