【差分约束】笔记

做题做不动来水博客了

1. 差分约束是什么

一般地,有一个长度为 n n n 的未知序列和 m m m 个限制条件,其中,每个限制条件都形如:

a x − a y ≤ k a_x-a_y\le k axayk

要求构造一组符合条件的序列,或者判断不存在这样的序列

2. 差分约束怎么写

我们可以将上面的式子写改写成 a x ≤ a y + k a_x\le a_y+k axay+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 axay+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<ayaxayaxay0,ayax0ayax1axay1ayax0

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);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值