差分约束 学习笔记

差分约束

差分约束就是:给你 n 个变量,m 个未知数,形成这样几组的不等式: x i x_i xi - y j y_j yj <= c k c_k ck;
然后让你求出一组解,使得所有约束条件都满足;

怎么解呢?可以把上面的式子变形一下: x i x_i xi <= y j y_j yj + c k c_k ck;

这是不是和图论最短路的式子非常相似呢?

所以可以连接 y i y_i yi x i x_i xi 的一条单向边,权值为 c k c_k ck ,然后求单源最短路即可,但是还要保证图的连通,所以可以加个超级源点,用 0 连接每个点,建立一条权值为 0 的边;

还有一种情况就是无解情况,什么时候会无解呢?

就是出现负环的情况,所以还要判断是否有负环,那么dij肯定是不可以了,spfa就成了最好的选择;

扩展:

如果是求 x i x_i xi<=0 的最大解,那么 y i y_i yi x i x_i xi 连边,跑最短路;

如果是求 x i x_i xi>0 的最小解,那么 x i x_i xi y i y_i yi 连边,跑最长路,这个和普通的最长路还不太一样,具体看代码;

模板题:洛谷·P5960 【模板】差分约束算法

第一种代码:

#include
#define LL long long
#define pa pair
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=50100;
const int mod=1e9;
int n,m;
struct Node{
	int to,nex,w;
}edge[N*2];
int head[N],cnt,dis[N],vis[N],sum[N];
void add(int p,int q,int w){
	edge[cnt].w=w,edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;
}
bool spfa(){
	for(int i=1;i<=n;i++) dis[i]=2e9;
	queue<int>qu;qu.push(0);
	while(!qu.empty()){
		int p=qu.front();qu.pop();
		vis[p]=0;sum[p]++;
		if(sum[p]>=n) return false;
		for(int i=head[p];~i;i=edge[i].nex){
			int q=edge[i].to;
			if(dis[q]>dis[p]+edge[i].w){//最短路
				dis[q]=dis[p]+edge[i].w;
				if(!vis[q]) qu.push(q),vis[q]=1;
			}
		}
	}
	return true;
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) add(0,i,0);
	for(int i=1;i<=m;i++){
		int a,b,c;scanf("%d%d%d",&a,&b,&c);
		add(b,a,c);
	}
	if(!spfa()) printf("NO\n");
	else for(int i=1;i<=n;i++) printf("%d ",dis[i]);
	return 0;
}

第二种代码:

#include
#define LL long long
#define pa pair
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=50100;
const int mod=1e9;
int n,m;
struct Node{
	int to,nex,w;
}edge[N*2];
int head[N],cnt,dis[N],vis[N],sum[N];
void add(int p,int q,int w){
	edge[cnt].w=w,edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;
}
bool spfa(){
	for(int i=1;i<=n;i++) dis[i]=-1;
	queue<int>qu;qu.push(0);
	while(!qu.empty()){
		int p=qu.front();qu.pop();
		vis[p]=0;sum[p]++;
		if(sum[p]>=n) return false;
		for(int i=head[p];~i;i=edge[i].nex){
			int q=edge[i].to;
			if(dis[q]<dis[p]+edge[i].w){//最长路 
				dis[q]=dis[p]+edge[i].w;
				if(!vis[q]) qu.push(q),vis[q]=1;
			}
		}
	}
	return true;
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) add(0,i,0);
	for(int i=1;i<=m;i++){
		int a,b,c;scanf("%d%d%d",&a,&b,&c);
		add(a,b,-c);
	}
	if(!spfa()) printf("NO\n");
	else for(int i=1;i<=n;i++) printf("%d ",dis[i]);
	return 0;
}

你可能感兴趣的:(#,差分约束,差分约束)