差分约束 + 01BFS

属于简单知识点的补档。

差分约束

差分约束系统 是一种特殊的 n 元一次不等式组,包含 n 个变量 x1,,xnm 个约束条件,每个约束条件形如 xixjck,其中 ck 是常数。我们要解决的问题是求出 x1,,xn 的一组解。

差分约束问题是最短路算法的一种巧妙应用。我们发现,差分约束系统中的每个约束条件 xixjck 都可以变形成 xixj+ck,这不由得让我们想到单源最短路中的三角形不等式 dis(v)dis(u)+w。因此,我们把每个未知数 xi 看作图中的一个节点,对每一个约束条件 xixjck 连边。

连边方法有两种:

  1. 连一条 xjxi 的边权为 ck 的边之后跑最短路,最终求出来的解是 xi0 时所有 x 的最大值
  2. 连一条 xixj 的边权为 ck 的边之后跑最长路,最终求出来的解是 xi0 时所有 x 的最小值

这两种方法不能混用。

注意这样连完边之后图不一定联通,此时我们只需定义一个 “超级源点” 0,并对所有 i 连 (0,i,0) 的边。然后,跑最短/最长路。

至于题目中要求判断无解,我们只需用 SPFA 判断是否存在负环即可。若存在负环,则无解;否则,xi=dis(i) 就是题目要求的一组解。

例 1 P5960 【模板】差分约束

constexpr int MAXN=5005;
int n,m,head[MAXN],tot;
struct{
	int v,to,w;
}e[MAXN<<1];
void addedge(int u,int v,int w){
	e[++tot]={v,head[u],w};
	head[u]=tot;
}
int dis[MAXN],cnt[MAXN];
bool vis[MAXN];
queue<int>q;
void spfa(){
	memset(dis,0x3f,sizeof(int)*(n+5));
	dis[0]=0;
	vis[0]=1;
	q.emplace(0);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].to)
			if(dis[e[i].v]>dis[u]+e[i].w){
				dis[e[i].v]=dis[u]+e[i].w;
				if(!vis[e[i].v]){
					vis[e[i].v]=1;
					q.emplace(e[i].v);
					if(++cnt[e[i].v]>n){
						puts("NO");
						exit(0);
					}
				}
			}
	}
}

int main(){
	n=read(),m=read();
	for(int i=1,u,v,w;i<=m;++i){
		u=read(),v=read(),w=read();
		addedge(v,u,w);
	}
	for(int i=1;i<=n;++i) addedge(0,i,0);
	spfa();
	for(int i=1;i<=n;++i) write(dis[i],' ');
	return putchar('\n'),fw,0;
}

例 2 P1993 小 K 的农场

把所有不等式转化为标准形式即可。注意连边方法一定一定不能弄混。

#include<bits/stdc++.h>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
using namespace std;

char buf[1<<20],*p1=buf,*p2=buf;
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
constexpr int MAXN=5005;
int n,m,head[MAXN],tot;
struct{
	int v,to,w;
}e[MAXN<<1];
void addedge(int u,int v,int w){
	e[++tot]={v,head[u],w};
	head[u]=tot;
}
int dis[MAXN],cnt[MAXN];
bool vis[MAXN];
queue<int>q;
void spfa(){
	memset(dis,0x3f,sizeof(int)*(n+5));
	dis[0]=0;
	vis[0]=1;
	q.emplace(0);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=e[i].to)
			if(dis[e[i].v]>dis[u]+e[i].w){
				dis[e[i].v]=dis[u]+e[i].w;
				if(!vis[e[i].v]){
					vis[e[i].v]=1;
					q.emplace(e[i].v);
					if(++cnt[e[i].v]>=n){
						puts("No");
						exit(0);
					}
				}
			}
	}
}

int main(){
	n=read(),m=read();
	for(int i=1,op,u,v;i<=m;++i){
		op=read(),u=read(),v=read();
		switch(op){
			case 1: addedge(u,v,-read());break;
			case 2: addedge(v,u,read());break;
			case 3: addedge(u,v,0),addedge(v,u,0);break;
		}
	}
	for(int i=1;i<=n;++i) addedge(0,i,0);
	spfa();
	return puts("Yes"),0;
}

01BFS

01BFS 用于解决边权只有 01 的最短路问题,复杂度是 O(n+m) 的,可以避免一般最短路算法的 log

方法:用一个双端队列 deque,把边权为 0 的边放到队首,边权为 1 的边放到队尾。

01BFS 复杂度的正确性建立在类似 Dijkstra 的松弛的基础上,所以它不能求最长路。

[AGC056C] 01 Balanced

这道题的主要部分在这篇题解里。这里只放它的 01BFS 部分。

void bfs(){
	memset(dis,-1,sizeof(int)*(n+5));
	dis[0]=0;
	deque<int>q;
	q.emplace_back(0);
	while(!q.empty()){
		int u=q.front();
		q.pop_front();
		for(int i=head[u];i;i=e[i].to){
			if(~dis[e[i].v]) continue;
			dis[e[i].v]=dis[u]+e[i].w;
			e[i].w?q.emplace_back(e[i].v):q.emplace_front(e[i].v);
		}
	}
}
posted @   Laoshan_PLUS  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示