差分约束

(本文假设读者已经有以下知识:最短路径的基本性 质、Bellman-Ford算法。)
    比如有这样一组不等式:
    

X1 - X2 <= 0
 X1 - X5 <= -1
 X2 - X5 <= 1
 X3 - X1 <= 5
 X4 - X1 <= 4
 X4 - X3 <= -1
 X5 - X3 <= -3
 X5 - X4 <= -3

不等式组(1)


    全都是两个未知数的差小于等于某个常数(大于等于也可以,因为左右乘 以-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。最后,我们在这张图上求一次单源最短路径,这些三角形不等式就会全部都满足了,因为它是最短路径问题的基本性质嘛。
    话说回来,所谓单源最短路径,当然要有一个源点,然后再求这个源点到其他所有点的最短路径。那么源点在哪呢?我们不妨自已造一个。以上面的不等式组为例, 我们就再新加一个未知数X0。然后对原来的每个未知数都对X0随便加一个不等式(这个不等式当然也要和其它不等式形式相同,即两个未知数的差小于等于某个 常数)。我们索性就全都写成Xn - X0 <= 0,于是这个差分约束系统中就多出了下列不等式:
  
 

X1 - X0 <= 0
 X2 - X0 <= 0
 X3 - X0 <= 0
 X4 - X0 <= 0
 X5 - X0 <= 0

不等式组(2)


    对于这5个不等式,也在图中建出相应的边。最后形成的图如下:

 


图1

     图中的每一条边都代表差分约束系统中的一个不等式。现在以V0为源点,求单源最短路径。最终得到的V0到Vn的最短路径长度就是Xn的一个解啦。从图1中 可以看到,这组解是{-5, -3, 0, -1, -4}。当然把每个数都加上10也是一组解:{5, 7, 10, 9, 6}。但是这组解只满足不等式组(1),也就是原先的差分约束系统;而不满足不等式组(2),也就是我们后来加上去的那些不等式。当然这是无关紧要的,因 为X0本来就是个局外人,是我们后来加上去的,满不满足与X0有关的不等式我们并不在乎。
    也有可能出现无解的情况,也就是从源点到某一个顶点不存在最短路径。也说是图中存在负权的圈。这一点我就不展开了,请自已参看最短路径问题的一些基本定理。

    其实,对于图1来说,它代表的一组解其实是{0, -5, -3, 0, -1, -4},也就是说X0的值也在这组解当中。但是X0的值是无可争议的,既然是以它作为源点求的最短路径,那么源点到它的最短路径长度当然是0了。因此,实 际上我们解的这个差分约束系统无形中又存在一个条件:

 

X0 = 0


    也就是说在不等式组(1)、(2)组成的差分约束系统的前提下,再把其中的一个未知数的值定死。这样的情况在实际问题中是很常见的。比如一个问题表面上给 出了一些不等式,但还隐藏着一些不等式,比如所有未知数都大于等于0或者都不能超过某个上限之类的。比如上面的不等式组(2)就规定了所有未知数都小于等 于0。
  
     对于这种有一个未知数定死的差分约束系统,还有一个有趣的性质,那就是通过最短路径算法求出来的一组解当中,所有未知数都达到最大值。下面我来粗略地证明 一下,这个证明过程要结合Bellman-Ford算法的过程来说明。
    假设X0是定死的;X1到Xn在满足所有约束的情况下可以取到的最大值分别为M1、M2、……、Mn(当然我们不知道它们的值是多少);解出的源点到每个 点的最短路径长度为D1、D2、……、Dn。
    基本的Bellman-Ford算法是一开始初始化D1到Dn都是无穷大。然后检查所有的边对应的三角形不等式,一但发现有不满足三角形不等式的情况,则 更新对应的D值。最后求出来的D1到Dn就是源点到每个点的最短路径长度。
    如果我们一开始初始化D1、D2、……、Dn的值分别为M1、M2、……、Mn,则由于它们全都满足三角形不等式(我们刚才已经假设M1到Mn是一组合法 的解),则Bellman-Ford算法不会再更新任合D值,则最后得出的解就是M1、M2、……、Mn。
    好了,现在知道了,初始值无穷大时,算出来的是D1、D2、……、Dn;初始值比较小的时候算出来的则是M1、M2、……、Mn。大家用的是同样的算法, 同样的计算过程,总不可能初始值大的算出来的结果反而小吧。所以D1、D2、……、Dn就是M1、M2、……、Mn。
  
     那么如果在一个未知数定死的情况下,要求其它所有未知数的最小值怎么办?只要反过来求最长路径就可以了。最长路径中的三角不等式与最短路径中相反:

 

d(v) >= d(u) + w(u, v)
 也就是 d(v) - d(u) >= w(u, v)


    所以建图的时候要先把所有不等式化成大于等于号的。其它各种过程,包括证明为什么解出的是最小值的证法,都完全类似。
 
     用到差分约束系统的题目有ZJU 2770,祝好运。

 

spfa模版:

传入:起点s,终点t,图list[];
传出:dis[] 每个点到s的最短路长度

#define MAX 20100
#define INF 2000000000
struct edge_t{
int v,w;
edge_t* next;
};
edge_t* list[MAX];
void init(){
for(int i=0;i<MAX;i++) list[i]=NULL;
}
void add(int u,int v ,int w){
edge_t* L; L=new edge_t;
L->v=v; L->w=w; L->next=list[u];
list[u]=L;
}
int dis[MAX],q[MAX],sum[MAX],n;
bool vis[MAX];

int spfa(int s,int t){
for(int i=0;i<MAX;i++) dis[i]=INF;
memset(sum,0,sizeof(sum));
memset(vis,0,sizeof(vis));
int head=0,tail=0;
q[tail++]=s; dis[s]=0,vis[s]=true; sum[s]++;
while(head!=tail){
int u=q[head];
for(edge_t* t=list[u];t;t=t->next){
int v=t->v,w=t->w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v]){
vis[v]=true; sum[v]++;
if(sum[v]>=n) return -1; //存在负环
q[tail++]=v;
if(tail==MAX-10) tail=0;
}
}
}
vis[u]=false; head++;
if(head==MAX-10) head=0;
}
if(dis[t]==INF) return -2; // 不存在到t的道路
return dis[t];
}


poj 3169

View Code
 1 大致题意:
2 有n头牛,按照序号1……n排队,多头牛可以站在同一个位置(暂且这么认为),也就是任意两头牛之间的距离都大于等于0。先给出ml组约束关系(u,v,w)代表第u牛和第v牛之间的距离要小于等于w。再给出md组关系(u,v,w),代表第u牛和第v牛之间的距离要大于等于w。求这n头牛排成的队伍能否符合以上的约束,不能的话(也就是出现负环),输出“-1”,如果距离是inf,输出“-2”。否则输出这个最短距离。
3
4 #include<iostream>
5 #include<cstdio>
6 #include<cstring>
7 using namespace std;
8 #define MAX 20100
9 #define INF 2000000000
10 struct edge_t{
11 int v,w;
12 edge_t* next;
13 };
14 edge_t* list[MAX];
15 void init(){
16 for(int i=0;i<MAX;i++) list[i]=NULL;
17 }
18 void add(int u,int v ,int w){
19 edge_t* L; L=new edge_t;
20 L->v=v; L->w=w; L->next=list[u];
21 list[u]=L;
22 }
23 int dis[MAX],q[MAX],sum[MAX],n;
24 bool vis[MAX];
25
26 int spfa(int s,int t){
27 for(int i=0;i<MAX;i++) dis[i]=INF;
28 memset(sum,0,sizeof(sum));
29 memset(vis,0,sizeof(vis));
30 int head=0,tail=0;
31 q[tail++]=s; dis[s]=0,vis[s]=true; sum[s]++;
32 while(head!=tail){
33 int u=q[head];
34 for(edge_t* t=list[u];t;t=t->next){
35 int v=t->v,w=t->w;
36 if(dis[v]>dis[u]+w){
37 dis[v]=dis[u]+w;
38 if(!vis[v]){
39 vis[v]=true; sum[v]++;
40 if(sum[v]>=n) return -1;
41 q[tail++]=v;
42 if(tail==MAX-10) tail=0;
43 }
44 }
45 }
46 vis[u]=false; head++;
47 if(head==MAX-10) head=0;
48 }
49 if(dis[t]==INF) return -2;
50 return dis[t];
51 }
52 int main(){
53 int a,b;
54 while(scanf("%d%d%d",&n,&a,&b)!=EOF){
55 init();
56 while(a--){
57 int u,v,w;
58 scanf("%d%d%d",&u,&v,&w);
59 add(u,v,w);
60 }
61 while(b--){
62 int u,v,w;
63 scanf("%d%d%d",&u,&v,&w);
64 add(v,u,-w);
65 }
66 printf("%d\n",spfa(1,n));
67 }
68 }

 


 参考文献:

http://bbezxcy.iteye.com/blog/1365897

转载于:https://www.cnblogs.com/louisnit/archive/2012/03/03/2378595.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值