[笔记]差分约束系统

[笔记]差分约束系统

算法用途

​ 当题面给出许多形如\(x_i - x_j ≤ c_k(c为常数)\)的不等式时,并让你求出一组满足条件的解时,就可以运用差分约束系统.

算法描述

​ 首先,我们对不等式进行变形,变成\(x_i ≤ x_j + c_k\)的形式,这时我们可以发现,这和求最短路时的式子(\(dis[v] ≥ dis[u] + w_{u→v})\))很像,但我们发现这两个式子的不等号方向相反,但这其实并不影响我们用最短路的方法来解决问题。

​ 首先我们假设有一个不等式组

\[\begin{cases} t_i ≤ t_{j'} + b\\ t_i ≤ t_{j''} + b\\ t_i ≤ t_{j'''} + b \end{cases} \]

那么要使\(t_i\)满足所有不等式并且取到最大值,则答案应该是\(min(t_{j'},t_{j''},t_{j'''}) + b\),因为如果要满足所有不等式则要取最小值(同小取小),实际情况\(t_j\)集合的元素可能不止\(3\)个,我们直接用\(t_j\)来表示所有\(t_j\)集合中的元素,则答案可以表示为\(t_i = min(t_j + b)\),这和\(SPFA\)中的式子(\(dis_{ti} = min(dis_{tj} + w_{i→j})\)一样,因此我们证明了差分约束的问题可以用最短路来解决。具体方法是连一条从\(j\)\(i\)的权值为\(b\)的有向边,再跑\(SPFA\)即可

​ 通过上面的推理我们知道当我们跑最短路时,可以求出满足条件的最大解,那么我们可以同理推出当我们用\(SPFA\)跑最长路时可以求出满足条件的最小值,那么究竟什么时候跑最短路,什么时候跑最长路呢?

例题讲解

​ 下面我们来看两道题目。T1,T2.

​ 在第一题中,题目的要求与上文我们举的例子相符合,因此是跑最短路。

​ 再来看第二题,题目要求的是让我们在所有区间\(a_i\)\(b_i\)中均选出不少于\(c_i\)个数来满足要求。我们假设\(t_i\)表示的是在前\(i\)个数中选了\(t_i\)个,那么我们可以得到如下不等式组:

\[\begin{cases} t_j - t_i ≥ c_{i,j}\\ t_{j'} - t_{i'} ≥ c_{i',j'}\\ t_{j''} - t_{i''} ≥ c_{i'',j''}\\ \end{cases} \]

类比上文的建图方法,我们以同样的方式建边,但要注意的是区间\(i→j\)所有选的数的个数应该表示为\(t_j - t_{i - 1}\)因为第\(i\)位的数字也可能被选中。但是这一个条件并不全, 我们知道一定会有\(0≤t_i - t_{i+1} ≤ 1\),因为可能存在第\(i+1\)位选或不选的情况,但由于一个建边的条件并不能满足两个不等式(上式可以拆分为两个不等式),所以我们通过两个条件来约束.根据拆分完的不等式来进行约束\(t_{i+1}-t_i ≥ 0\)\(t_{i+1} ≥ t_i + 0\)因此从\(i\)连一条到\(j\)的权值为\(0\)的有向边;第二个不等式是\(t_i - t_{i+1} ≤ 1\)\(t_i ≤ t_{i+1} + 1\),因此从\(i+1\)连一条到\(i\)的权值为\(0\)的有向边,这样就没有漏过任何一个约束条件了。

代码

T1

#include <bits/stdc++.h>
using namespace std;
struct node{int to,next,w;}e[50010];
int fir[50010],tot = 0,times[50010],n,m;
void add(int x,int y,int z){
	tot++;
	e[tot].w = z;
	e[tot].to = y;
	e[tot].next = fir[x];
	fir[x] = tot;
	return;
}
bool in[50010];
int dis[50010];
bool spfa(int x){
	queue < int > q;
	for(int i = 1;i <= n;i++)dis[i] = 1e9;
	while(!q.empty())
		q.pop();
	memset(in,false,sizeof(in));
	memset(times,0,sizeof(times));
	in[x] = true;
	times[x]++;
	dis[x] = 0;
	q.push(x);
	while(!q.empty()){
		int k = q.front();
		q.pop();in[k] = false;
		for(int i = fir[k];i;i = e[i].next){
			if(dis[e[i].to] > dis[k] + e[i].w){
				dis[e[i].to] = dis[k] + e[i].w;
				if(!in[e[i].to]){
					q.push(e[i].to);
					times[e[i].to]++;
					in[e[i].to] = true;
					if(times[e[i].to] > n)
						return false;
				}
			}
		}
	}
	return true;
}
int main(){
	cin>>n>>m;
	for(int i = 1;i <= m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		add(y,x,z);
	}
	for(int i = 1;i <= n;i++){
		add(0,i,0);
	}
	if(!spfa(0)){
		cout<<"NO"<<endl;
		return 0;
	}
	for(int i = 1;i <= n;i++){
		cout<<dis[i]<<" ";
	}
	cout<<endl;
	return 0;
}

T2

#include <bits/stdc++.h> 
using namespace std;
struct node{
	int to,next,w;
}edge[150010];
const int inf = INT_MAX;
long long fir[150010],tim[150010],dis[150010],tot,n,maxx,minn;
bool in[150010];
void add(int x,int y,int z){
	tot++;
	edge[tot].to = y;
	edge[tot].next = fir[x];
	edge[tot].w = z;
	fir[x] = tot;
}
void spfa(){
	queue < int > q;
	while(!q.empty())q.pop();
	memset(in,false,sizeof(in)); 
	memset(tim,0,sizeof(tim));
	memset(dis,-63,sizeof(dis));
	dis[minn] = 0;in[minn] = true;tim[minn] = 1;q.push(minn);
	while(!q.empty()){
		int x = q.front();q.pop();
		in[x] = false;
		for(int i = fir[x];i != -1;i = edge[i].next){
			if(dis[edge[i].to] < dis[x] + edge[i].w){
				dis[edge[i].to] = dis[x] + edge[i].w;
				if(!in[edge[i].to]){
					tim[edge[i].to]++;
					if(tim[edge[i].to] > maxx)return;
					in[edge[i].to] = true;
					q.push(edge[i].to);
				}
			}
		}
	}
	return;
}
int main(){
		scanf("%d",&n);
		memset(fir,-1,sizeof(fir));
		memset(edge,0,sizeof(edge));
		maxx = -1;minn = inf;
		tot = 0;
		for(int i = 1;i <= n;i++){
			long long x,y,z;
			scanf("%lld%lld%lld",&x,&y,&z);
			if(x > y)swap(x,y);
			add(x - 1,y,z);
			maxx = max(maxx,y);
			minn = min(minn,x - 1);
		}
		for(int i = minn;i <= maxx;i++){
			add(i,i + 1,0);
			add(i + 1,i,-1);
		}
		spfa();
		printf("%d\n",dis[maxx]);
	return 0;
}

完结撒花

posted @ 2020-09-13 22:06  czyczy  阅读(115)  评论(0编辑  收藏  举报