从差分约束到最短路径(POJ 3169)

话不多说,先来看这么几个线性规划的约束方程:

x1x20x1x51x2x51x3x15x4x14x4x31x5x33x5x43

像这样不等式左边是变量1~n相减,右边是常数的不等式就称为差分约束,下面给出一个差分约束的通式:
xjxibk

这里的 1I , jn , ij ,并且 1km
咋看起来,这个差分约束好像和我们的最短路径没太大关系。但如果把不等式的左边的两个变量看做两个顶点,不等式的右边的常数看做这两个点路径的权重,就可以把一个个的不等式看做一条条边,就可以用这些边和点构成一个图,这似乎就和我们的最短扯上一点关系了。
如果还不明显的话,我们讲上面的不等式以矩阵的形式展示出来:
这里写图片描述
左边的0,1,-1矩阵,我们再做一个转置,得到:
这里写图片描述
这样就清楚了,每一列代表每一条边的进出情况,每一行就代表图中的每个点。1表示进入这个点,-1表示从这个点出去。而右边的向量当然就代表着每条边的权重了。
这时候,我们再额外加一个 v0 , v0 与图中的所有点相连,并且权重为0。于是乎,我们的约束图就构建完成了。所谓的约束图,就是指的这么个带权重的有向图,定义如下:
这里写图片描述
我们得到的约束图如下:
这里写图片描述
那么,得到了图,这个约束方程的解就是图中每一个点到其它点的最短距离了,得到我们新的定理:
这里写图片描述
这里的 δ(v0,vn) 代表 v0 vn 的最距离,这里就不细讲证明,证明就直接贴图了:
这里写图片描述
这里写图片描述

我们来用利用差分约束来解决我们的实际问题:
POJ 3169 http://poj.org/problem?id=3169
Description

Like everyone else, cows like to stand close to their friends when queuing for feed. FJ has N (2 <= N <= 1,000) cows numbered 1..N standing along a straight line waiting for feed. The cows are standing in the same order as they are numbered, and since they can be rather pushy, it is possible that two or more cows can line up at exactly the same location (that is, if we think of each cow as being located at some coordinate on a number line, then it is possible for two or more cows to share the same coordinate).

Some cows like each other and want to be within a certain distance of each other in line. Some really dislike each other and want to be separated by at least a certain distance. A list of ML (1 <= ML <= 10,000) constraints describes which cows like each other and the maximum distance by which they may be separated; a subsequent list of MD constraints (1 <= MD <= 10,000) tells which cows dislike each other and the minimum distance by which they must be separated.

Your job is to compute, if possible, the maximum possible distance between cow 1 and cow N that satisfies the distance constraints.
Input

Line 1: Three space-separated integers: N, ML, and MD.

Lines 2..ML+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at most D (1 <= D <= 1,000,000) apart.

Lines ML+2..ML+MD+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at least D (1 <= D <= 1,000,000) apart.
Output

Line 1: A single integer. If no line-up is possible, output -1. If cows 1 and N can be arbitrarily far apart, output -2. Otherwise output the greatest possible distance between cows 1 and N.
Sample Input

4 2 1
1 3 10
2 4 20
2 3 3
Sample Output
27

这道题主要讲的三个条件抽象出来就是:
(这里的A,B表示两头牛,L,D用来区别关系好和关系差)
1.每头牛按编号顺序排好:d[i+1]>=d[i]
2.ML对两只关系好的牛之间的距离不超过D: d[AL]-d[BL] D
3.MD对两个关系不好的牛之间的距离不小于D: d[AD]-d[BD] D
我们在对第三个条件转化一下,得到以下三个式子:

d[i+1]>=d[i]d[AL]d[BL]Dd[BD]d[AD]D

嘿嘿,我们的差分约束出来了。
根据上面的说明,我们可以将这个差分约束转化为图,并且该图的最短路径是该约束条件的一个可行解,但是,为什么点1-N的最短路径在这里能够表示1-N的最大距离呢?
我们对于任意一条权值为w的边e=(v,u),都有如下的公式:

d(v)+wd(u)

d(v),d(u)分别表示点v,u到图的起点(自己定)的最短距离。表示的是即使其它边到点u的距离肯定比到点u的最短距离要大或者等于。
好的,这里我们令,v=1,u=N,做一个简单的变形:
d(N)d(1)w

不等式的左边是我们要求的第N头牛到第1头牛的距离,右边是点1到N的路径长。
那么,我们要求左边能取到的最大值,实际就是w的一个最小值(把w看成一个函数),举一个简单的例子:
d(N)d(1)3d(N)d(1)5d(N)d(1)7

对于这个不等式组,d(N)-d(1)能取到的最大值就是右边的最小值3,然后将数字换成我们的w,一样的道理。于是乎,这里w的最小值就是点1到N的一个最短距离,我们就成功将这道题转化为了最短路径问题。
因为这里出现负权值,我们采用Bellman-Ford算法来解决:
这里要注意两点:
1. 如果Bellman-Ford处理一个点两次,说明出现了负权圈,无法找到最大距离,即无法找到相应的排列,返回-1,代码中则是我们初始化过的dist[0]<0.
2. 如果点N的最大距离没有被更新,说明给的条件不能影响到点N,即随便放置都可以,返回-2,代码中即体现dist[N]=INF
样例中的图
这里写图片描述
以下则是AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

struct edge
{
    int st;
    int to;
    int cost;
};

const int MAX=1000005;
const int MAX2=10005;
const int INF=0x0fffffff;
edge ML[MAX2];
edge MD[MAX2];
int N,M1,M2;

int dist[MAX2*2];


int main()
{
    scanf("%d %d %d",&N,&M1,&M2);
    for(int i=0;i<M1;++i)
    {
        int st,to,cost;
        scanf("%d %d %d",&st,&to,&cost);
        st--;to--;
        ML[i].st=st;
        ML[i].to=to;
        ML[i].cost=cost;
    }
    for(int i=0;i<M2;++i)
    {
        int st,to,cost;
        scanf("%d %d %d",&st,&to,&cost);
        st--;to--;
        cost=-cost;
        MD[i].st=to;
        MD[i].to=st;
        MD[i].cost=cost;
    }

    for(int i=0;i<N;++i)
    {
        dist[i]=INF;
    }

    dist[0]=0;
    //进行最短路径处理,从顶点出发到所有的点,复杂度为V*E,所以应该遍历更新所有的点
    for(int k=0;k<N;++k)
    {
        //dist[i+1]>=dist[i],牛按照大小顺序排列
        for(int i=0;(i+1)<N;++i)
        {
              if(dist[i+1]<INF)  dist[i]=min(dist[i],dist[i+1]);
        }

        //ML的牛的最大距离
        for(int i=0;i<M1;++i)
        {
            edge& e=ML[i];
            if(dist[e.st]<INF)
            {
                dist[e.to]=min(dist[e.to],dist[e.st]+e.cost);
            }
        }

        //MD的牛的最大距离
        for(int i=0;i<M2;++i)
        {
            edge& e=MD[i];
            if(dist[e.st]<INF)
            {
                dist[e.to]=min(dist[e.to],dist[e.st]+e.cost);
            }
        }
    }
    int res=dist[N-1];
    if(dist[0]<0)
    {
        //不可行 
        res=-1;
    }
    else if(res==INF)
    {
        res=-2;
    }
    printf("%d\n",res);
    return 0;
}

Reference:《算法导论》,《挑战程序设计竞赛》

猜你喜欢

转载自blog.csdn.net/github_33873969/article/details/78842143