做题做不动来水博客了
1. 差分约束是什么
一般地,有一个长度为 n n n 的未知序列和 m m m 个限制条件,其中,每个限制条件都形如:
a x − a y ≤ k a_x-a_y\le k ax−ay≤k
要求构造一组符合条件的序列,或者判断不存在这样的序列
2. 差分约束怎么写
我们可以将上面的式子写改写成 a x ≤ a y + k a_x\le a_y+k ax≤ay+k,此时,联想一下最短路算法:
说真的,正常人第一次接触这玩意估计都想不到最短路
int y=ver[i],z=edge[i];//链式前向星模板,用它的人应该都看得懂
if(dis[y]>dis[x]+z){
更新代码
}
这一串代码就是三角形不等式的应用
所谓三角形不等式,大致来说:在一张图中,用 dis
数组来表示从一个起始节点
s
s
s 到
i
i
i 节点的路径边权和,若对于任意一条边(x
为起点,y
为终点,z
为边权),满足 dis[y]<=dis[x]+z
,则 dis[y]
为起始节点
s
s
s 到
i
i
i 节点的最短路
证明显然,若 dis[y]>dis[x]+z
,则说明 dis[x]+z
可以用来更新 dis[y]
,那么此时,dis[y]
就不是最小的,而按照最短路的定义,dis[y]
应该是最小的,矛盾
每当我们找到会产生矛盾的这样一条边时,我们都会拿去更新最短路
三角形不等式与
a
x
≤
a
y
+
k
a_x\le a_y+k
ax≤ay+k 高度相似,所以,我们可以利用三角形不等式:利用这些限制条件来建图,然后去找最短路,若最终找到了最短路,说明这些条件是符合三角形不等式的,那么,序列中第
i
i
i 个数就应该是 dis[i]
,反之,则说明无解
而这里,
a
x
a_x
ax 对应 dis[y]
,
a
y
a_y
ay 对应 dis[x]
,
k
k
k 代表 z
,所以,在建图时,应该建从
y
y
y 到
x
x
x,边值为
z
z
z 的一条有向边
注意:
z
z
z 有可能为负数,所以Dijkstra
不能用。一般而言,大部分差分约束的题是不会卡SPFA
的(我要在这里点名批评一道题~)
顺带提一提一些如何将某些式子转化为差分约束式的式子:
a x = a y ⇒ a x − a y ≤ 0 , a y − a x ≤ 0 a x > a y ⇒ a y − a x ≤ − 1 a x < a y ⇒ a x − a y ≤ − 1 a x ≥ a y ⇒ a y − a x ≤ 0 \begin{aligned}a_x=a_y &\Rightarrow a_x-a_y\le0,a_y-a_x\le0 \\ a_x>a_y& \Rightarrow a_y-a_x\le-1 \\ a_x<a_y& \Rightarrow a_x-a_y\le-1 \\ a_x\ge a_y&\Rightarrow a_y-a_x\le0\end{aligned} ax=ayax>ayax<ayax≥ay⇒ax−ay≤0,ay−ax≤0⇒ay−ax≤−1⇒ax−ay≤−1⇒ay−ax≤0
3. 优化
在使用朴素的SPFA时,我们所使用的是队列,然而,我们可以将队列改成双端队列,然后,将 dis[y]
较小的放在队列前面,较大的放在队列后面,每一次取出时取前面的
原理:我们每一次在更新 dis[y]
时,若 dis[y]
被更新的越小,那么它后续被又更新的可能性就会更小,而为了使 dis[y]
更小,我们就要让 dis[x]
尽可能的小,所以,在每一次将新元素塞入队列中时,就要尽可能的将小的塞前面
代码:
if(q.empty()){//如果双端队列为空,直接塞
q.push_front(y);
}else{
if(dis[y]<=dis[q.front()]){//小的塞前,大的塞后
q.push_front(y);
}else{
q.push_back(y);
}
}