差分约束系统:
最近新学的,是在学最短路时看到的。可以转化为最短路来求解,刚开始一点也不懂,那玩意儿怎么这么神奇!
之后两天,一直搞这个东西,这才稍微理解了一点。
先从定义出发。
给你一大堆不等式,然后叫你求给定不等式的最值。(比较难理解,必须搞一个很牛逼的例子)
例如:
①
x2–x1≤2
②
x3–x1≤5
③
x3–x2≤4
④
x4–x2≤1
⑤
x4–x3≤3
⑥
x5–x3≤6
⑦
x5–x4≤7
现在让求在满足上面每个不等式的条件下,
x5–x1
的最大值?
- ② + ⑥ ⇒ x5–x1≤11
- ② + ⑤ + ⑦ ⇒ x5–x1≤15
- ① + ④ + ⑦ ⇒ x5–x1≤10
- ① + ③ + ⑥ ⇒ x5–x1≤12
- ① + ③ + ⑤ + ⑦ ⇒ x5–x1≤16
所以,要满足上面的所有条件,只能 x5–x1≤10 。(稍微想想应该很好理解)那么,所求的最大值就是 10 。(这个应该很简单)
全是两个未知数的差小于等于一个常数(大于等于其实是一样的,移项或者乘个 −1 就变成了小于等于),这样的不等式组就叫 差分约束系统。
对于这个不等式组解的情况,要么无解,要么有无数多解。因为,一旦有了一组解,加上任意一个常数k,各个不等式依旧成立。
接下来就是怎么将这个问题转化为最短路求给定不等式的最值了。
最短路的几个算法就不再详细说了。
通常求最短路时都会有一个
dis[]
数组,
dis[i]表示i
节点到源点的最短路径是多长。
那么,对于任意两个相邻节点
u,v
来说,
dis[u]+cost[u][v]≥dis[v]
,这个不等式是一定满足的。(想想Dijkstra 是怎么通过一个节点u来松弛相邻节点v的?)
对不等式移项,
dis[v]–dis[u]≤cost[u][v]
。在没求出最短路之前,
dis[v]和dis[u]
就是未知数,而
cost[u][v]
正好就是常数。和上面的 差分约束系统 惊人的相似!
求最短路时的每步松弛操作都是满足不等式的,所以,解 差分约束系统 就可以类比为求最短路,由此,最短路算法就变成了 解差分约束系统的利器。
对于差分例子的不等式组中的每一个不等式
X–Y<=C
,建立一条由
Y
指向
对于上面的不等式组,建图如下:
未知数
xi对应i
号节点。
上面
5个x5–x1
的不等式对应路径如下:
1−>3−>5⇒dis[5]=11
1−>3−>4−>5⇒dis[5]=15
1−>2−>4−>5⇒dis[5]=10
1−>2−>3−>5⇒dis[5]=12
1−>2−>3−>4−>5⇒dis[5]=16
最小值 10 对应着所求答案的最大值。
差分约束系统如何用图论方法解决就先说这么多,需要慢慢领悟。
小总结:
形如
X–Y≤C
的不等式组,一般会让求
Xt–Xs
的最大值(即求
Xt–Xs≤T的那个T
值,求最小值完全无意义啊!)。
初始化
dis[i]=INF
, 然后对于每个
Xi–Yi≤Ci
,建立一条
Yi
指向
Xi
边权为
Ci
的边,以
Xs
为源点,求最短路,最后得出的
dis[Xt]
即为所求的 最大值 (
T
)。若
形如
X–Y≥C
的不等式组,一般会让求
Xt–Xs
的最小值(即求
Xt–Xs≥T
的那个
T
值,求最大也完全无意义!)
注意:
<或>
也要转化为
≤或≥
,例如
X–Y<C
,可转化为
X–Y≤C–1
。
下面通过例题来加深印象:
这一阵做POJ上的 差分约束系统,做的心累啊!!!
题目链接:
题目大意:
有
n
个孩子分糖果,给出
数据范围:
n≤30000m≤150000
解题思路:
令孩子
i
的糖果数为
典型的 差分约束系统,对于每个关系,建立
A−>B
边权为
C
的有向边。以
外话:
这可能是道假题!一般求最短路,用SPFA+queue就可以了,然而,TLE到死!queue的原因?不是,改成手写队列之后RE到死!什么SPFA?!都是假的,,,改成Dijkstra,轻松过掉!
去搜了一把,什么?!有大佬用stack写SPFA!还有这种操作?!NB勒!
AC代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 200005;
int n, m;
int all;
int pre[2 * MaxN + 5], last[MaxN + 5], other[2 * MaxN + 5];
int cost[2 * MaxN + 5];
int dis[MaxN + 5];
struct Node {
int id, d;
Node () {}
Node (int a, int b) : id(a), d(b) {}
bool friend operator < (Node x, Node y) {
return x.d > y.d;
}
};
void build(int x, int y, int w) {
pre[++all] = last[x];
last[x] = all;
other[all] = y;
cost[all] = w;
}
void Dijkstra(int s) {
for(int i = 1; i <= n; i++) dis[i] = inf;
dis[s] = 0;
priority_queue <Node> pq;
pq.push(Node(s, 0));
while(!pq.empty()) {
Node now = pq.top();
pq.pop();
int ed = last[now.id];
while(ed != -1) {
int dr = other[ed];
if(dis[now.id] + cost[ed] < dis[dr]) {
dis[dr] = dis[now.id] + cost[ed];
pq.push(Node(dr, dis[dr]));
}
ed = pre[ed];
}
}
}
int main()
{
while(scanf("%d %d", &n, &m) != EOF)
{
all = -1; memset(last, -1, sizeof(last));
for(int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
build(u, v, w); //建立u -> v, 边权为w的有向边
}
Dijkstra(1);
printf("%d\n", dis[n]);
}
return 0;
}
这里还有一道:POJ-3169