POJ 3169

题目连接: layout

本题用到差分约束的方法,整理了一下网上关于差分约束的资料。

 

1.概念

           如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统。

2.问题

          差分约束系统属于线性规划问题。在一个差分约束系统中,线性规划矩阵A的每一行包含一个1和一个-1,A的所有其他元素都为0。因此,由Ax≤b给出的约束条件是m个差分约束集合,其中包含n个未知元。每个约束条件为如下形式的简单线性不等式:xj-xi≤bk(1≤i, j≤n,1≤k≤m)。如下图5维向量x满足8个不等式的差分约束,我们可以把未知量x1,x2,x3,x4,x5化为如下8个不等式:

      差分约束系统                        差分约束系统

我们可以发现当x=(x1,x2,…,xn)是差分约束系统Ax≤b的一个解,d为任意常数。则x+d=(x1+d,x2+d,…,xn+d)也是该系统Ax≤b的一个解。因此这个不等式组要么无解,要么就有无数组解。

3. 解决方法

          求解差分约束系统,可以转化成图论的单源最短路径问题。观察xj-xi<=bk,会发现它类似最短路中的三角不等式d[v] <= d[u] + w[u,v],即d[v] - d[u] <= w[u,v]。因此,以每个变量xi为结点,对于约束条件xj-xi<=bk,连接一条边E(i,j),边权为bk。求单源最短路径,必须有一个源点,然后再求这个源点到其他所有点的最短路径,我们可以增加一个原点s与所有其他点相连,边权均为0,xi - x0 <= 0。上图中的不等式于是转化为下图中的有向图:

差分约束系统

 

补充:

如果还没有完全理解,我们可以先来看一个简单的情况,如下三个不等式:

B - A <= c      (1)

C - B <= a      (2)

C - A <= b      (3)

      我们想要知道C - A的最大值,通过(1) + (2),可以得到 C - A <= a + c,所以这个问题其实就是求min{b, a+c}。将上面的三个不等式按照 三-1 数形结合 中提到的方式建图,如图三-2-1所示。

                                 图三-2-1

      我们发现min{b, a+c}正好对应了A到C的最短路,而这三个不等式就是著名的三角不等式。将三个不等式推广到m个,变量推广到n个,就变成了n个点m条边的最短路问题了。

 

解的存在性

      上文提到最短路的时候,会出现负权圈或者根本就不可达的情况,所以在不等式组转化的图上也有可能出现上述情况,先来看负权圈的情况,如图三-3-1,下图为5个变量5个不等式转化后的图,需要求得是X[t] - X[s]的最大值,可以转化成求s到t的最短路,但是路径中出现负权圈,则表示最短路无限小,即不存在最短路,那么在不等式上的表现即X[t] - X[s] <= T中的T无限小,得出的结论就是 X[t] - X[s]的最大值 不存在。

                                                图三-3-1

      再来看另一种情况,即从起点s无法到达t的情况,如图三-3-2,表明X[t]和X[s]之间并没有约束关系,这种情况下X[t] - X[s]的最大值是无限大,这就表明了X[t]和X[s]的取值有无限多种。

图三-3-2

      在实际问题中这两种情况会让你给出不同的输出。综上所述,差分约束系统的解有三种情况:1、有解;2、无解;3、无限多解;

 

最大值 => 最小值

      然后,我们将问题进行一个简单的转化,将原先的"<="变成">=",转化后的不等式如下:

         B - A >= c      (1)

         C - B >= a      (2)

         C - A >= b      (3)

      然后求C - A的最小值,类比之前的方法,需要求的其实是max{b, c+a},于是对应的是图三-2-1从A到C的最长路。同样可以推广到n个变量m个不等式的情况。

      

不等式标准化

      如果给出的不等式有"<="也有">=",又该如何解决呢?很明显,首先需要关注最后的问题是什么,如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成"<="的形式,建图后求最短路;相反,如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成">=",建图后求最长路。

      如果有形如:A - B = c 这样的等式呢?我们可以将它转化成以下两个不等式:

     A - B >= c      (1)

     A - B <= c      (2)

       再通过上面的方法将其中一种不等号反向,建图即可。

       最后,如果这些变量都是整数域上的,那么遇到A - B < c这样的不带等号的不等式,我们需要将它转化成"<="或者">="的形式,即 A - B <= c - 1。

差分约束的经典应用

1、线性约束

        线性约束一般是在一维空间中给出一些变量(一般定义位置),然后告诉你某两个变量的约束关系,求两个变量a和b的差值的最大值或最小值。

     【例题1】N个人编号为1-N,并且按照编号顺序排成一条直线,任何两个人的位置不重合,然后给定一些约束条件。

       X(X <= 100000)组约束Ax Bx Cx(1 <= Ax < Bx <= N),表示Ax和Bx的距离不能大于Cx。

       Y(X <= 100000)组约束Ay By Cy(1 <= Ay < By <= N),表示Ay和By的距离不能小于Cy。

       如果这样的排列存在,输出1-N这两个人的最长可能距离,如果不存在,输出-1,如果无限长输出-2。

      像这类问题,N个人的位置在一条直线上呈线性排列,某两个人的位置满足某些约束条件,最后要求第一个人和最后一个人的最长可能距离,这种是最直白的差分约束问题,因为可以用距离作为变量列出不等式组,然后再转化成图求最短路。

      令第x个人的位置为d[x](不妨设d[x]为x的递增函数,即随着x的增大,d[x]的位置朝着x正方向延伸)。

      那么我们可以列出一些约束条件如下:

      1、对于所有的Ax Bx Cx,有 d[Bx] - d[Ax] <= Cx;

      2、对于所有的Ay By Cy,有 d[By] - d[Ay] >= Cy;

      3、然后根据我们的设定,有 d[x] >= d[x-1] + 1 (1 < x <= N)  (这个条件是表示任何两个人的位置不重合)

     而我们需要求的是d[N] - d[1]的最大值,即表示成d[N] - d[1] <= T,要求的就是这个T。

     于是我们将所有的不等式都转化成d[x] - d[y] <= z的形式,如下:

      1、d[Bx]  -  d[Ax]    <=    Cx

      2、d[Ay]  -  d[By]    <=  -Cy

      3、d[x-1] -    d[x]    <=    -1

     对于d[x] - d[y] <= z,令z = w(y, x),那么有 d[x] <= d[y] + w(y, x),所以当d[x] > d[y] + w(y, x),我们需要更新d[x]的值,这对应了最短路的松弛操作,于是问题转化成了求1到N的最短路。

       对于所有满足d[x] - d[y] <= z的不等式,从y向x建立一条权值为z的有向边。

      然后从起点1出发,利用SPFA求到各个点的最短路,如果1到N不可达,说明最短路(即上文中的T)无限长,输出-2。如果某个点进入队列大于等于N次,则必定存在一条负环,即没有最短路,输出-1。否则T就等于1到N的最短路。


区间约束

     【例题2】给定n(n <= 50000)个整点闭区间和这个区间中至少有多少整点需要被选中,每个区间的范围为[ai, bi],并且至少有ci个点需要被选中,其中0 <= ai <= bi <= 50000,问[0, 50000]至少需要有多少点被选中。

      例如3 6 2 表示[3, 6]这个区间至少需要选择2个点,可以是3,4也可以是4,6(总情况有 C(4, 2)种 )。

 

      这类问题就没有线性约束那么明显,需要将问题进行一下转化,考虑到最后需要求的是一个完整区间内至少有多少点被选中,试着用d[i]表示[0, i]这个区间至少有多少点能被选中,根据定义,可以抽象出 d[-1] = 0,对于每个区间描述,可以表示成d[ bi ]  - d[ ai - 1 ] >= ci,而我们的目标要求的是 d[ 50000 ] - d[ -1 ] >= T 这个不等式中的T,将所有区间描述转化成图后求-1到50000的最长路。

      这里忽略了一些要素,因为d[i]描述了一个求和函数,所以对于d[i]和d[i-1]其实是有自身限制的,考虑到每个点有选和不选两种状态,所以d[i]和d[i-1]需要满足以下不等式:  0 <= d[i] - d[i-1] <= 1   (即第i个数选还是不选)

      这样一来,还需要加入 50000*2 = 100000 条边,由于边数和点数都是万级别的,所以不能采用单纯的Bellman-Ford ,需要利用SPFA进行优化,由于-1不能映射到小标,所以可以将所有点都向x轴正方向偏移1个单位(即所有数+1)。

 

未知条件约束

      未知条件约束是指在不等式的右边不一定是个常数,可能是个未知数,可以通过枚举这个未知数,然后对不等式转化成差分约束进行求解。

     【例题3】

在一家超市里,每个时刻都需要有营业员看管,R(i)  (0 <= i < 24)表示从i时刻开始到i+1时刻结束需要的营业员的数目,现在有N(N <= 1000)个申请人申请这项工作,并且每个申请者都有一个起始工作时间 ti,如果第i个申请者被录用,那么他会连续工作8小时。

现在要求选择一些申请者进行录用,使得任何一个时刻i,营业员数目都能大于等于R(i)。

       i = 0 1 2 3 4 5 6 ... 20 21 22 23 23,分别对应时刻 [i, i+1),特殊的,23表示的是[23, 0),并且有些申请者的工作时间可能会“跨天”。

       a[i] 表示在第i时刻开始工作的人数,是个未知量

       b[i] 表示在第i时刻能够开始工作人数的上限, 是个已知量

       R[i] 表示在第i时刻必须值班的人数,也是已知量

       那么第i时刻到第i+1时刻还在工作的人满足下面两个不等式(利用每人工作时间8小时这个条件):

       当 i >= 7,        a[i-7] + a[i-6] + ... + a[i] >= R[i]                                     (1)

       当 0 <= i < 7,  (a[0] + ... + a[i]) + (a[i+17] + ... + a[23]) >= R[i]              (2)

 

       对于从第i时刻开始工作的人,满足以下不等式:

       0 <= i < 24,    0 <= a[i] <= b[i]                                                            (3)

       令 s[i] = a[0] + ... + a[i],特殊地,s[-1] = 0

       上面三个式子用s[i]来表示,如下:

       s[i] - s[i-8] >= R[i]                               (i >= 7)                                      (1)

       s[i] + s[23] - s[i+16] >= R[i]               (0 <= i < 7)                                  (2)

       0 <= s[i] - s[i-1] <= b[i]                     (0 <= i < 24)                                (3)

       

      仔细观察不等式(2),有三个未知数,这里的s[23]就是未知条件,所以还无法转化成差分约束求解,但是和i相关的变量只有两个,对于s[23]的值我们可以进行枚举,令s[23] = T, 则有以下几个不等式:

      

      s[i] - s[i-8] >= R[i]

      s[i] - s[i+16] >= R[i] - T

      s[i] - s[i-1] >= 0

      s[i-1] - s[i] >= -b[i]

      对于所有的不等式 s[y] - s[x] >= c,建立一条权值为c的边 x->y,于是问题转化成了求从原点-1到终点23的最长路。

      但是这个问题比较特殊,我们还少了一个条件,即:s[23] = T,它并不是一个不等式,我们需要将它也转化成不等式,由于设定s[-1] = 0,所以 s[23] - s[-1] = T,它可以转化成两个不等式:

      s[23] - s[-1] >= T

      s[-1] - s[23] >= -T

      将这两条边补到原图中,求出的最长路s[23]等于T,表示T就是满足条件的一个解,由于T的值时从小到大枚举的(T的范围为0到N),所以第一个满足条件的解就是答案。

      最后,观察申请者的数量,当i个申请者能够满足条件的时候,i+1个申请者必定可以满足条件,所以申请者的数量是满足单调性的,可以对T进行二分枚举,将枚举复杂度从O(N)降为O(logN)。

 

 

个人理解:

         差分约束题目的关键在于将其转化为最短路,而后用最短路的方法来处理。本人喜欢用 SPFA + 链式前向星 来做最短路的题目,对于 SPFA 无法判断负环的情况,可以引入一个数组来记录某一点进入队列的次数,如果超过所有点的总和,那么就肯定存在负环。SPFA 时间复杂度为 o(KE).

poj3169 代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int maxn = 1010;
const int maxe = 20010;
const int INF = 2147483646;
int head[maxn];
int vis[maxn];
int cnt[maxn];
int dist[maxn];

struct Node{
	int to;
	int w;
	int ne;
}e[maxe];
int tot;
void add(int u,int v,int w){
	e[tot].to = v;
	e[tot].ne = head[u];
	e[tot].w = w;
	head[u] = tot ++;
}
bool SPFA(int st,int n){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(i == st){
			q.push(i);
			vis[i] = 1;
			dist[i] = 0;
			cnt[i] = 1;
		}
		else {
			vis[i] = 0;
			dist[i] = INF;
			cnt[i] = 0;
		}
	}
	while(!q.empty()){
		int now = q.front();
		q.pop();
		for(int i=head[now];~i;i=e[i].ne){
			int to = e[i].to;
			if(dist[to] > dist[now] + e[i].w){
				dist[to] = dist[now] + e[i].w;
				if(!vis[to]){
					q.push(to);
					vis[to] = 1;
					if(++cnt[to] > n)
					return false;
				}
			}
		}
		vis[now] = 0;
	}
	return true;
}
int main()
{
	int n;
	int Ml,Md;
	int a,b,c;
	while(scanf("%d%d%d",&n,&Ml,&Md) != EOF){
		tot = 0;
		memset(head,-1,sizeof(head));
		while(Ml --){
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,c); 
		}
		while(Md --){
			scanf("%d%d%d",&a,&b,&c);
			add(b,a,-c); 
		}
		if(!SPFA(1,n)) {
			printf("-1\n");
		}
		else if(dist[n] == INF){
			printf("-2\n");
		} 
		else {
			printf("%d\n",dist[n]);
		}
	}
	return 0;
}

文章转载于:

http://www.cppblog.com/menjitianya/archive/2015/11/19/212292.html

https://www.cnblogs.com/PJQOOO/p/4330405.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值