差分约束(Difference constraint)

差分约束

1.差分约束系统(system of difference constraints)

如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。

求解差分约束系统,可以转化成图论的单源最短路径(或最长路径)问题。
——百度百科
如下列的不等式组

x1-x5≤-1
x2-x5≤1
x3-x1≤5
x4-x1≤4
x4-x3≤-1
x5-x3≤-3
x5-x4≤-3

比如1式 x1-x5≤-1,其中一个未知数x1与x5的差小于等于-1,就相当于设两个未知数x,y差为某一常数,由这些不等式构成的不等式方程组为差分约束系统。

2.例题

小K的农场

关于洛谷一道蓝题。
P1993 小K的农场
但因为在另一篇题解中写过了,所以不多做阐述,解题看好差分约束条件就行了。
小K的农场(差分约束)题解

然后是另外两道题

蒜头君的银行卡

Description

虽然蒜头君并没有多少钱,但是蒜头君办了很多张银行卡,共有 n 张,以至于他自己都忘记了每张银行卡里有多少钱了。他只记得一些含糊的信息,这些信息主要以下列三种形式描述:

银行卡 a 比银行卡 b 至少多 c 元。
银行卡 a 比银行卡 b 至多多 c 元。
银行卡 a 和银行卡 c 里的存款一样多。
但是由于蒜头君的记忆有些差,他想知道是否存在一种情况,使得银行卡的存款情况和他记忆中的所有信息吻合。

Input
第一行输入两个整数 n 和 m,分别表示银行卡数目和蒜头君记忆中的信息的数目。(1≤n,m≤10000)

接下来 m 行:

如果每行第一个数是 1,接下来有三个整数 a,b,c,表示银行卡 a 比银行卡 b 至少多 c元。

如果每行第一个数是 2,接下来有三个整数 a,b,c,表示银行卡 a 比银行卡 b 至多多 c元。

如果每行第一个数是 3,接下来有两个整数 a,b,表示银行卡 a 和 b 里的存款一样多。(1≤n,m,a,b,c≤10000)

Output
如果存在某种情况与蒜头君的记忆吻合,输出Yes,否则输出No。

Sample Input 1

3 3
3 1 2
1 1 3 1
2 2 3 2
Sample Output 1

Yes

很明显,题目中出现了几个约束条件:
银行卡 a 比银行卡 b 至少多 c 元。
银行卡 a 比银行卡 b 至多多 c 元。
银行卡 a 和银行卡 c 里的存款一样多。

转化为不等式方程组:

a-b>=c
a-b<=c
a=c

找到了解题的关键,然后将差分约束转化为最短路求解 。

#include
using namespace std;

const int maxn=10100;
const int inf=0x3f3f3f3f;

int n,m;

struct node{
	int v,w;
	node(){ }
	node(int _v,int _w){
		v=_v;
		w=_w;
	}
};

vector  g[maxn];
int dst[maxn];
queue  qu;
bool inq[maxn];
int cnt[maxn];


int add(int u,int v,int w){
	g[u].push_back(node(v,w));
//	g[v].push_back(node());
}

int spfa(int u){
	memset(inq,0,sizeof inq);
	memset(dst,inf,sizeof dst);
	memset(cnt,0,sizeof cnt);
	dst[u]=0;
	qu.push(u);
	inq[u]=1;
	cnt[u]=1;
	while(!qu.empty()){
		u=qu.front();
		qu.pop();
		inq[u]=0;
		for(int i=0;idst[u]+w){
				dst[v]=dst[u]+w;
				if(!inq[v]){
					qu.push(v);
					inq[v]=1;
					cnt[v]++;
					if(cnt[v]>n+1){
					return 0;	
					}
				}
			}
		}
	}
	return 1;
}


int main(){
	cin >> n >> m;
	
	for(int i=0;i> d;
		if(d==1){
			cin >>a>>b>>c;
			g[a].push_back(node(b,-c));
		}else if(d==2){
			cin >>a>>b>>c;
			g[b].push_back(node(a,-c));
		}else{
			cin >>a>>b;
			g[a].push_back(node(b,0));
			g[b].push_back(node(a,0));
		}
	}
	
	for(int i=1;i<=n;i++){
		add(0,i,0);
	}
	if(spfa(0)){
	cout << "Yes"<
蒜头君当大厨
Description

蒜头君苦练厨艺,终于成为了某高档酒店的大厨。

每天上班,蒜头君会被要求做 n 份菜。既然是高档酒店,那么客人们当然是很讲究的,尤其对于上菜的时间有很多要求。客人们的要求被分成下列四种:

菜品 a 的上菜时间必须比菜品 b 的上菜时间早 d 分钟或者更早。

菜品 a 的上菜时间必须比菜品 b 的上菜时间迟 d 分钟或者更迟。

菜品 a 的上菜时间在 d 分钟以后(包含 d 分钟)。

菜品 a 的上菜时间在 d 分钟之前(包含 d 分钟)。

蒜头君的上班时间记为 0 分钟。为了节约时间,在满足客人们要求的情况下,蒜头君希望最后上的一道菜的时间尽可能的早。(每道菜的上菜时间必须不早于蒜头君的上班时间)


Input
第一行输入一个整数 n,表示一共需要上 n 道菜。

第二行输入一个整数 m,表示客人们的要求数量。

接下里 m 行,每行先输入一个整数 op。

如果 op=1,表示描述里的第 1 种要求,后面跟着三个整数 a,b,d。
如果 op=2,表示描述里的第 2 种要求,后面跟着三个整数 a,b,d。
如果 op=3,表示描述里的第 3 种要求,后面跟着两个整数 a,d。
如果 op=4,表示描述里的第 4 种要求,后面跟着两个整数 a,d。

Output
如果蒜头君能满足客人们的要求,输出最后一道菜的上菜时间;否则输出一行 'I can't'。

数据范围和约定

对于所有的数据:1≤n,m≤20000,1≤∣d∣≤10000 ,1≤a,b≤n,a≠b。

样例解释 1

1,2,3 的上菜时间分别为 0,2,12,这样能满足输入客人们的所有要求,并且时间最短。



Sample Input 1 

3
5
2 3 2 10
2 2 1 2
2 3 2 5
1 2 3 7
3 3 9
Sample Output 1

12
Sample Input 2 

3
4
3 1 3
2 3 1 9
2 1 3 -1
1 1 2 5
Sample Output 2

I can't
Sample Input 3 

17
20
2 6 3 -21
1 8 2 54
3 7 -95
4 11 44
1 5 15 40
3 9 1
3 3 30
3 8 23
2 9 12 -15
4 13 61
2 3 7 31
1 5 10 -15
2 16 1 43
2 12 3 -79
2 14 16 -51
3 6 48
4 7 0
2 10 11 -59
2 12 17 -29
3 4 10
Sample Output 3

77

与蒜头君的银行卡差不多,都是找到差分约束条件:
菜品 a 的上菜时间必须比菜品 b 的上菜时间早 d 分钟或者更早。

菜品 a 的上菜时间必须比菜品 b 的上菜时间迟 d 分钟或者更迟。

菜品 a 的上菜时间在 d 分钟以后(包含 d 分钟)。

菜品 a 的上菜时间在 d 分钟之前(包含 d 分钟)。

#include
using namespace std;

const int maxn=20010;

int n,m;

struct node{
	int v,w;
	node(){ }
	node(int _v,int _w){
		v=_v;
		w=_w;
	}
};

vector  g[maxn];
int dst[maxn];
queue  qu;
bool inq[maxn];
int cnt[maxn];
int flag;

int add(int u,int v,int w){
	g[u].push_back(node(v,w));
//	g[v].push_back(node());
}

void spfa(int u){
	memset(inq,0,sizeof inq);
	memset(dst,0x80,sizeof dst);
	memset(cnt,0,sizeof cnt);
	dst[u]=0;
	qu.push(u);
	inq[u]=1;
	cnt[u]=1;
	while(!qu.empty()){
		u=qu.front();
		qu.pop();
		inq[u]=0;
		for(int i=0;in+1){
						return;
					}
					if(cnt[v]==n+1){
						flag=1;
					}
				}
			}
		}
	}
	return;
}


int main(){
	cin >> n >> m;
	
	for(int i=0;i> d;
		if(d==1){
			cin >>a>>b>>c;
			g[a].push_back(node(b,c));
		}else if(d==2){
			cin >>a>>b>>c;
			g[b].push_back(node(a,c));
		}else if(d==3){
			cin >>a>>b;
			g[0].push_back(node(a,b));
		}else{
			cin >>a>>b;
			g[0].push_back(node(0,-b));
		}
	}
	
	for(int i=1;i<=n;i++){
		add(0,i,0);
	}
int ans=0;
spfa(0);

if(flag){
	cout<<"I can't";
	return 0;
}
 
 for(int i=1;i<=n;i++){
 ans=max(ans,dst[i]);
}
cout << ans;
	return 0;
}

你可能感兴趣的:(图论)