洛谷 P5960 【【模板】差分约束算法】/差分约束算法入门

啊这,为什么一道看上去完全跟图论无关的题有图论标签。


正题(以下没有特殊说明,均求的是最短路):

差分约束系统&&转化: 顾名思义,差分约束系统就是给你很多个形如(x_1-x_2leqslant c_k)的不等式(其中c为常数),让你求出一组解或者判断无解。看上面的式子,把它变成这样:(x_1leqslant x_2+c_k),然后可以发现(x_1)的最大值就为(x_2+c_k),这样一来,我们就可以把(x_2)(x_1)连一条有向边,边权为(c_k)来跑最短路,跑到的最短路的值,就是一组答案(至于为什么最大值可以跑最短路,后面有解释)。

为什么是正确的呢? 在多个不等式中,对于一个数(x),我们可以通过自己所在的不等式来确定自身范围,而一个不等式有两个未知数,另外一个未知数又通过其他不等式与其他未知数相关系起来,这就搭建起来了一条条边,组合成图,从任意一个点出发,一个一个点的满足,最终肯定会到达x的,这样也就得出了一组解。

差分约束系统也可以跑最长路 差分约束系统中,最短路和最长路的区别就是:最短路是求的所有解中最大的一组,而最长路相反。看这样一组式子: (egin{cases}x_2-x_1leqslant c_1\x_3-x_2leqslant c_2\x_3-x_1leqslant c_3end{cases}) 我们可以得出这样两个式子:(egin{cases}x_3leqslant x_1+c_3\x_3leqslant x_1+c_1+c_2end{cases}) ,可以得出,约束(x_3)的有两个条件,正好图里面也有两条路。我们要求(x_3)的最大值时,取的肯定是(x_1+c_3)(x_1+c_1+c_2)里面的最小值,这样才能满足所有的约束条件,也就是最短路了。最长路求的最小解同理(最长路的建边方式有所不同,具体建边方式可以根据转化那个板块推一下)。

什么情况无解: 最短路里面有负环时无解。再看一组式子:(egin{cases}x_1-x_3leqslant 3\x_2-x_1leqslant -5\x_3-x_2leqslant -3end{cases}),这种情况下,转化成图后就会出现负环,而这组式子最后可以得出一个式子:(x_3leqslant x_3-5),跑最短路时,为了满足这个条件,就会一直更新一直更新,也就是出现了负环。所以,有负环的时候,不等式组无解。

接下来就是代码时间咯(当时(SPFA)一直(RE),也不知道为什么,后来索性写了(Ford),还是(Ford)香啊~):

#include <bits/stdc++.h>
using namespace std;
struct node{
	int l , r , w;
};
node e[5010];
int n , m;
int dis[5010];
int main(){
	cin >> n >> m;
	for(int i = 1; i <= m; i++) cin >> e[i].r >> e[i].l >> e[i].w;	//注意l和r反过来了 
	for(int i = 1; i <= n - 1; i++)
		for(int j = 1; j <= m; j++)
			dis[e[j].r] = min(dis[e[j].r] , dis[e[j].l] + e[j].w);	//松弛 
	for(int j = 1; j <= m; j++)
		if(dis[e[j].r] > dis[e[j].l] + e[j].w){	//判断是否有负环 
			cout << "NO";
			return 0;
		}
	for(int i = 1; i <= n; i++) cout << dis[i] << " ";
	return 0;
}
原文地址:https://www.cnblogs.com/bzzs/p/13223914.html