首页 > 代码库 > zoj2770 差分约束问题

zoj2770 差分约束问题

总的开说差分约束问题就是给出一系列不等式然后求问某一式子的最大值或者最小值。

差分约束问题详解:

    比如有这样一组不等式: 
   
X1 - X2 <= 0 
X1 - X5 <= -1 
X2 - X5 <= 1 
X3 - X1 <= 5                   不等式组(1) 
X4 - X1 <= 4 
X4 - X3 <= -1 
X5 - X3 <= -3 
X5 - X4 <= -3 

    全都是两个未知数的差小于等于某个常数(大于等于也可以,因为左右乘以-1就可以化成小于等于)。这样的不等式组就称作差分约束系统。 
    这个不等式组要么无解,要么就有无数组解。因为如果有一组解{X1, X2, ..., Xn}的话,那么对于任何一个常数k,{X1 + k, X2 + k, ..., Xn + k}肯定也是一组解,因为任何两个数同时加一个数之后,它们的差是不变的,那么这个差分约束系统中的所有不等式都不会被破坏。 
    
    差分约束系统的解法利用到了单源最短路径问题中的三角形不等式。即对于任何一条边u -> v,都有: 

d(v) <= d(u) + w(u, v) 

    其中d(u)和d(v)是从源点分别到点u和点v的最短路径的权值,w(u, v)是边u -> v的权值。 
    显然以上不等式就是d(v) - d(u) <= w(u, v)。这个形式正好和差分约束系统中的不等式形式相同。于是我们就可以把一个差分约束系统转化成一张图,每个未知数Xi对应图中的一个顶点Vi,把所有不等式都化成图中的一条边。对于不等式Xi - Xj <= c,把它化成三角形不等式:Xi <= Xj + c,就可以化成边Vj -> Vi,权值为c。

在建立图的时候应该与目标函数的符号相同,即目标函数为>=,不等式的符号也应该变为>=,再根据不等式建立图;

相反,如果是<=,不等式也应该全部变为<=,再建立图;

(一致性)符号是>=或者<=,并非<和>


上文就讲了如何根据不等式建立图,哪到底和问题的不等式的最大值或者最小值有什么关联呢?

如题目要求d(v) - d(u)的最小值,可以转换成d(v) - d(u) >=M,而根据上文得知M为u->v的权值。

要想上式子成立,则应有min(d(v) - d(u))>=max(M),即转成了求u->v最长的路径即式子的最小值

相同,如果求d(v) - d(u)的最大值,这应该求u->v的最小路径

对于差分不等式,a - b <= c ,求的是最短路,得到的是最大值;

对于差分不等式 a - b >= c ,求的是最长路,得到的是最小值。


关于补不补充源点:

当d(v) 和d(u)都已在图中存在,则不需要补充源点。

当图没有目标源点的时候或者需要连接整个图的时候,则需要补充源点。并补充关系。


如果图中存在负权值回路,则求出来的最短路径没有意义。也就是说不等式无解。所以要从是否有负权值回路进而判断有无解。


zoj求最小值:(差分不等式a-b>=c,求最长路,得到最小值)

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include<string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int dist[1010];
int n,m,a,b,c;
struct edge
{
	int to,cost;
}cur;
vector<edge>vec[1010];
inline void read(int &m)//int
{
	int x=0,f=1;char ch=getchar();//int
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	m=x*f;
}
bool spfa()  //(差分不等式a-b>=c,求最长路,得到最小值)
{
	int cnt[1010];bool vis[1010];
	for(int i=1;i<=n;i++)
		dist[i]=-1111111111;
	memset(cnt,0,sizeof(cnt));
	memset(vis,0,sizeof(vis));
	vis[0]=1;
	dist[0]=0;
	queue<int>que;
	que.push(0);
	while(!que.empty())
	{
		int v=que.front();
		que.pop();
		if(++cnt[v]>=n)
			return 0;
		vis[v]=0;
		for(int i=0;i<vec[v].size();i++)
		{
			edge temp=vec[v][i];
			if(dist[temp.to]<dist[v]+temp.cost)
			{
				dist[temp.to]=dist[v]+temp.cost;
				if(!vis[temp.to])
				{
					vis[temp.to]=1;
					que.push(temp.to);
				}
			}
		}
	}
	return 1;
}
int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		for(int i=0;i<=n;i++)
			vec[i].clear();
		for(int i=1;i<=n;i++)
		{
			read(a);
			cur.to=i;
			cur.cost=0;
			vec[i-1].push_back(cur);  //Ai>=0  S(i-1)-Si<=0     
			cur.to=i-1;
			cur.cost=-a;
			vec[i].push_back(cur); //Ai<=a   Si-S(i-1)<=0
			cur.to=i;
			cur.cost=0;
			vec[0].push_back(cur);  //Si-S0>=0     //0点为新添上去的源点,与其他节点建立关系
		}
		for(int i=1;i<=m;i++)
		{
			read(a),read(b),read(c);
			cur.to=b;
			cur.cost=c;
			vec[a-1].push_back(cur);  //Sb-Sa-1>=c
		}
		if(spfa())
			printf("%d\n",dist[n]);
		else 
			printf("Bad Estimations\n");
	}
	return 0;
}