差分约束系统

一.差分约束

问题是给定x1,x2,……,xn,然后给定出若干限制
xi-xj<=c,然后求解/无解
xi-xj>=c可以转换成xj-xi<=-c;
xi-xj=c,可以转换为xi-xj<=c,xi-xj>=c;

最暴力的想法就是一开始给所有的变量都赋值为0,然后每次都暴力循环所有的限制,如果有限制不满足,直接改变一个限制的值
其实这个想法就相当于是bellman-ford算法,时间复杂度是o(nm)的
然后我们再解决其中的一个问题,就是非负解,我们可以引入一个变量x0
然后xi-x0>=0,相当于x0-xi<=0
这样做之后,得到一组解,然后就可以将每个解都减去x0,这样x0就变成0,而其他等式依然满足条件,即xi-x0>=0均满足条件,所以可以得到非负整数解

注意:xu-xv<=w,相当于u连向v的边,权值为W

然后上结论:
最短路对应的是最大值,然后最小值就是给xi变成-xi,xi-xj<=w --> (-xi)-(-xj)<=w --> xj-xi<=w,即j连向i,并且边权为w的最短路,因为xi取到最大值,那么-xi一定是相当于取到最小值的

所以最短路就是求最大值,将边的方向反过来后的最短路就是最小值(求解的时候也要反回来,将xi-x0改为x0-xi)
或者是这样求解最小值
xu-xv<=w,改为xv>=xu-w,然后跑最长路,xv=max(xu-w,xv);

https://www.luogu.com.cn/record/194743808

求最大值,最短路

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;
 
const int N=1e6+10,INF=1e16,mod=1e9+7,M=2e5+10;

typedef pair<int,int> PII;

vector<array<int,3>>E;
int x[N];

void slove(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		E.push_back({u,v,w});
	}
	
	for(int i=1;i<=n;i++){
		E.push_back({0,i,0});//x0-xi<=0,即xi-x0>=0 
	}
	
	for(int i=1;i<=n;i++) x[i]=1<<30;//bellman-ford算法,所有点一开始均为最大值
	
	for(int i=0;i<=n;i++){
		for(auto [u,v,w]:E){
			x[u]=min(x[u],x[v]+w);//x[u]<=x[v]+w的限制
		}
	}
	
	
	for(auto [u,v,w]:E){
		if(x[u]>x[v]+w){//有负环的情况
			cout<<"NO"<<endl;
			return ;
		}
	}
	
	for(int i=1;i<=n;i++) cout<<x[i]-x[0]<<" ";
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
//	cin>>T;
	while(T--) slove();
}

求最小值,最短路写法

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;
 
const int N=1e6+10,INF=1e16,mod=1e9+7,M=2e5+10;

typedef pair<int,int> PII;

vector<array<int,3>>E;
int x[N];

void slove(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		E.push_back({v,u,w});//建反边
	}
	
	for(int i=1;i<=n;i++){
		E.push_back({i,0,0});//x0-xi<=0,即xi-x0>=0,建反边
	}
	
	for(int i=1;i<=n;i++) x[i]=1<<30;//bellman-ford算法,所有点一开始均为最大值
	
	for(int i=0;i<=n;i++){
		for(auto [u,v,w]:E){
			x[u]=min(x[u],x[v]+w);//x[u]<=x[v]+w的限制
		}
	}
	
	
	for(auto [u,v,w]:E){
		if(x[u]>x[v]+w){//有负环的情况
			cout<<"NO"<<endl;
			return ;
		}
	}
	
	for(int i=1;i<=n;i++) cout<<x[0]-x[i]<<" ";//反过来
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
//	cin>>T;
	while(T--) slove();
}

求最小值,最长路写法

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;
 
const int N=1e6+10,INF=1e16,mod=1e9+7,M=2e5+10;

typedef pair<int,int> PII;

vector<array<int,3>>E;
int x[N];

void slove(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		E.push_back({u,v,w});
	}
	
	for(int i=1;i<=n;i++){
		E.push_back({0,i,0});
	}
	
	for(int i=1;i<=n;i++) x[i]=-(1<<30);//bellman-ford算法,最长路取max是负无穷大,最短路取min是正无穷大
	
	/*
	x[v]>=x[u]-w,即x[v]=max(x[v],x[u]-w);
	*/
	for(int i=0;i<=n;i++){
		for(auto [u,v,w]:E){
			x[v]=max(x[v],x[u]-w);//x[u]<=x[v]+w的限制
		}
	}
	
	
	for(auto [u,v,w]:E){
		if(x[u]>x[v]+w){//有负环的情况
			cout<<"NO"<<endl;
			return ;
		}
	}
	
	for(int i=1;i<=n;i++) cout<<x[i]-x[0]<<" ";//反过来
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
//	cin>>T;
	while(T--) slove();
}

可以解决区间和问题,比如说[l,r]内的值的和大于等于x,还有a[l]>=x等都可以转化为前缀形式,然后差分约束求解

posted @ 2024-12-14 23:34  MENDAXZ  阅读(1)  评论(0编辑  收藏  举报