差分约束

差分约束

讲解

如果一个不等式组由 (n) 个变量和 (m) 个约束条件组成,形成 (m) 个形如(x[ j ]-x[ i ]≤k)(i,j∈[1,n])(k) 为常数)的不等式,则称其为差分约束系统。换句话说,差分约束系统就是求解一组变量的不等式组的算法。

(d_v-d_u<=w) 和 spfa 中的松弛操作 (d_v<=d_u+w) 非常像
只要没有负环则能得到⼀组合法的{(d_i)} ,则原问题有解且 {(d_i)} 就是⼀组可行解

做题步骤

1.判断是让求至多(最短路)还是至少(最长路)

2.按照最短/长路松弛操作,对应题目约束条件连边

3.看题目中一些特殊要求,继续完善连边

4.建立超级源点0,向每个点连边权为 0 的边

最后的答案输出:

一般是让输出 dis[n] , dis[1~n], 无解:最短路有负环,最长路有正环(用cnt[ ]判断每个点取出次数是不是≥n )

spfa

k;相当于限制(x[ i ])的最大/小值

连边后求最短路
(x[ i ]−x[ j ]≤k) 变形为 (x[ i ]≤k+x[ j ]),即从 (j)(i) 连一条边权为 (k) 的边。加入超级源点后求最短路,得到(x[i] ≤ k) 所有 (x) 最大解。

连边后求最长路
(x[ i ]−x[ j ]≥k) 变形为 (x[ j ]≤x[ i ]-k),即从 (i)(j) 连一条边权为 (-k) 的边。加入超级源点后求最长路,得到(x[i]≥k) 所有 (x) 最小解。

判负环spfa必然退化为O(nm)

其他未写的题目:

附上一篇有超详细解析和题目的blog

模板

一定注意建超级源点

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=5005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,m;
int hd[N],to[N<<1],tot,nxt[N<<1],w[N<<1];
inline void add(int x,int y,int z) {
	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;	
}
queue<int>q;
int dis[N],cnt[N];
bool vis[N];
bool spfa() {
	q.push(0);
	dis[0]=0;
	cnt[0]=1;
	while(q.size()) {
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=hd[x];i;i=nxt[i]) {
			int y=to[i];
			if(dis[y]>dis[x]+w[i]) {
				dis[y]=dis[x]+w[i];
				if(++cnt[y]>n) return 1;
				if(!vis[y]) vis[y]=1,q.push(y);
			}
		}
	}
	return 0;
}
int main() {
	memset(dis,0x3f,sizeof(dis));
	n=read();m=read();
	for(int i=1;i<=m;i++) {
		int u=read(),v=read(),w=read();
		add(v,u,w);
	}
	for(int i=1;i<=n;i++)
		add(0,i,0);
	if(spfa()) puts("NO");
	else {
		for(int i=1;i<=n;i++)
			printf("%d ",dis[i]);
	}
	return 0;
}

种树

求最少种树量——最长路

(dis[y]<dis[x-1]+w[i]) —— 区间 ([x,y])至少种 (w[i]) 棵树

for(int i=0;i<=n;i++){
	if(i!=0)add(i-1,i,0),dis[i]=-inf; // [i-1,i]最少种0棵
	if(i!=n)add(i,i-1,-1);//[i-1,i]最多种1棵
}
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=50005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
const int inf=0x3f3f3f3f;
int n,m;
int hd[N],to[N<<1],tot,nxt[N<<1],w[N<<1];
inline void add(int x,int y,int z) {
	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;	
}
queue<int>q;
int dis[N],cnt[N];
bool vis[N];
bool spfa() {
	q.push(0);
	dis[0]=0;
	cnt[0]=1;
	while(q.size()) {
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=hd[x];i;i=nxt[i]) {
			int y=to[i];
			if(dis[y]<dis[x]+w[i]) {
				dis[y]=dis[x]+w[i];
				if(++cnt[y]>n) return 1;
				if(!vis[y]) vis[y]=1,q.push(y);
			}
		}
	}
	return 0;
}
int main() {
	n=read();m=read();
	for(int i=1;i<=m;i++) {
		int u=read(),v=read(),w=read();
		add(u-1,v,w);
	}
	for(int i=0;i<=n;i++) {
		if(i) add(i-1,i,0),dis[i]=-inf;
		if(i!=n) add(i,i-1,-1);
	}
	if(spfa()) puts("NO");
	else printf("%d ",dis[n]);
	return 0;
}

也可用贪心和线段树优化

POJ 3169 Layout

https://www.luogu.com.cn/problem/P4878

注意限制 i+1 ---> i 连 0 (牛可以在同一个位置)

从0开始跑spfa判断图是不是联通的——洛谷毒瘤hack

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=2005;
const int M=1000005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n,m1,m2;
int hd[N],to[M],tot,nxt[M],w[M],cnt[N];
inline void add(int x,int y,int z) {
	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
bool vis[N];
long long dis[N];
bool spfa(int x) {
	queue<int>q;
	for(int i=0;i<=n;i++) dis[i]=0x3f3f3f3f,vis[i]=0;
	q.push(x);dis[x]=0;
	while(!q.empty()) {
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=hd[x];i;i=nxt[i]) {
			int y=to[i];
			if(dis[y]>dis[x]+w[i]) {
				dis[y]=dis[x]+w[i];
				if(!vis[y]) {
					vis[y]=1;q.push(y);
					if(++cnt[y]==n) {
						puts("-1");return 1;
					}
				}				
			}
		}
	}
	return 0;
}
int main() {
	n=read();m1=read();m2=read();
	int a,b,c;
	for(int i=1;i<=m1;i++) {
		a=read();b=read();c=read();
		add(a,b,c);
	}
	for(int i=1;i<=m2;i++) {
		a=read();b=read();c=read();
		add(b,a,-c);
	}
	for(int i=1;i<=n;i++)
		add(0,i,0);
	for(int i=1;i<n;i++)
		add(i+1,i,0);
	if(spfa(0)) return 0;
	spfa(1);
	printf("%lld
",dis[n]==0x3f3f3f3f?-2:dis[n]);
	return 0;
}

小 K 的农场

  • 农场a比农场b至少多种植了c个单位的作物, (dis[a]-dis[b]>c) , (dis[b]<dis[a]-c) ; (a)(b)连一条 (-c)
  • 农场a比农场b至多多种植了c个单位的作物,(dis[a]-dis[b]<c) , (dis[a]<dis[b]+c) ; (b)(a)连一条 (c)
  • 农场(a)与农场(b)种植的作物数一样多。(a-->b)连一条(0) (b-->a)连一条(0)

然后快乐AC

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=100005;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
const int inf=0x3f3f3f3f;
int n,m;
int hd[N],to[N<<1],tot,nxt[N<<1],w[N<<1];
inline void add(int x,int y,int z) {
	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;	
}
queue<int>q;
int dis[N],cnt[N];
bool vis[N];
bool spfa() {
	memset(dis,0x3f,sizeof(dis));
	q.push(0);
	dis[0]=0;
	cnt[0]=1;
	while(q.size()) {
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=hd[x];i;i=nxt[i]) {
			int y=to[i];
			if(dis[y]>dis[x]+w[i]) {
				dis[y]=dis[x]+w[i];
				if(++cnt[y]>n) return 1;
				if(!vis[y]) vis[y]=1,q.push(y);
			}
		}
	}
	return 0;
}
int main() {
	n=read();m=read();
	for(int i=1;i<=m;i++) {
		int op=read(),u=read(),v=read(),ww;
		if(op==1) ww=read(),add(u,v,-ww);
		else if(op==2) ww=read(),add(v,u,ww);
		else add(u,v,0),add(v,u,0);
	}
	for(int i=1;i<=n;i++) add(0,i,0);
	puts(spfa()?"No":"Yes");
	return 0;
}

糖果

这题卡邻接表???vector 可过

好像正解是tarjan —— this

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<utility>
using namespace std;
#define N 300005
#define inf 0x3f3f3f3f
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x;
}
vector < pair<int,int> > g[N];
queue < int > q;
int n,m;
int dis[N],cnt[N];
bool vis[N],f;
long long ans;
int main(){
	n=read();m=read();
	for(int i=1,op,x,y;i<=m;i++){
		op=read();x=read();y=read();
		if(op==1) g[x].push_back(make_pair(y,0)),g[y].push_back(make_pair(x,0));
		else if(op==2) g[x].push_back(make_pair(y,1));
		else if(op==3) g[y].push_back(make_pair(x,0));
		else if(op==4) g[y].push_back(make_pair(x,1));
		else g[x].push_back(make_pair(y,0));
		if(x==y&&(op==2||op==4)){puts("-1");return 0;}
	}
	for(int i=1;i<=n;i++)
		g[0].push_back(make_pair(i,1));
	memset(dis,-0x3f,sizeof(dis));
	q.push(0);vis[0]=1;dis[0]=0;
	while(!q.empty()){
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=0,siz=g[x].size();i<siz;i++){
			int y=g[x][i].first,w=g[x][i].second;
			if(dis[y]<dis[x]+w){
				dis[y]=dis[x]+w;
				if(++cnt[y]==n){ puts("-1"); return 0;}
				if(!vis[y]){
					vis[y]=1;q.push(y);
				}
			}
		}
	}
	for(int i=1;i<=n;i++)
		ans+=dis[i];
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/ke-xin/p/13596940.html