以HDU3592作为背景
题意: 有n个人排队, 然后有x对人的关系, 他们之间的距离最大不能超过c, 有y对人的关系, 他们之间的距离最小不能小于c, 问1-n之间的最大距离是多少?
板子
const int maxn=1e4+5;
const int inf = 0x3f3f3f3f; //用这个可以直接mem
int n;
int head[maxn], cnt, dis[maxn];
bool vis[maxn];
int times[maxn];
struct node
{
int to,next,w;
}e[maxn<<1];
void add(int u, int v, int w)
{
e[cnt] = (node){v,head[u],w};
head[u] = cnt++;
}
void init() {
cnt = 0;
Fill(haed, -1);
}
bool spfa(int st,int ed)
{
queue<int >q;
Fill(dis,inf); Fill(vis,0); Fill(times,0);
dis[st]=0; vis[st] = true; times[st]++;
q.push(st);
while(!q.empty()){
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; ~i ;i = e[i].next) {
int to = e[i].to;
if(dis[to] > dis[u] + e[i].w){
dis[to] = dis[u] + e[i].w;
if(times[to] > n)
return false;
else if(!vis[to]){
times[to]++;
vis[to] = true;
q.push(to);
}
}
}
}
return true;
}
// 其中a - c <= ? 是求最短路
//注意差分约数所有的不等式都是要同一方向.! 这个很重要. 即全部转化成题目要求的方向.
//还要注意题目中的隐藏条件以及应该转化成求什么路!!!
void solve()
{
int x,y;
scanf("%d%d%d",&n,&x,&y);
init();
for(int i = 1; i <= x ; i ++) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
add(a, b, c); //b - a <= c
}
for(int i = 1; i <= y; i ++) {
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(b, a, -c); // b - a >= c => a - b <= -c
}
if (!spfa(1, n)) printf("-1\n"); //有环是无解
else{
if(dis[n] == inf) printf("-2\n"); //到不了是多解,表示a和c没有关系
else printf("%d\n",dis[n]);
}
}
然后继续回到单个不等式上来,观察 x[i] - x[j] <= a[k], 将这个不等式稍稍变形,将x[j]移到不等式右边,则有x[i] <= x[j] + a[k],然后我们令a[k] = w(j, i),再将不等式中的i和j变量替换掉,i = v, j = u,将x数组的名字改成d(以上都是等价变换,不会改变原有不等式的性质),则原先的不等式变成了以下形式:d[u] + w(u, v) >= d[v]。
这时候联想到SPFA中的一个松弛操作:
if (d[u] + w(u, v) < d[v]) {
d[v] = d[u] + w(u, v);
}
对比上面的不等式,两个不等式的不等号正好相反,但是再仔细一想,其实它们的逻辑是一致的,因为SPFA的松弛操作是在满足小于的情况下进行松弛,力求达到d[u] + w(u, v) >= d[v],而我们之前令a[k] = w(j, i),所以我们可以将每个不等式转化成图上的有向边:
// 这篇博客讲的非常好: 传送门
1: 对于 xi - xj <= c 的就是j -> i 建一条长度为c的有向路径.
2、三角不等式
如果还没有完全理解,我们可以先来看一个简单的情况,如下三个不等式:
B - A <= c (1)
C - B <= a (2)
C - A <= b (3)
我们想要知道C - A的最大值,通过(1) + (2),可以得到 C - A <= a + c,所以这个问题其实就是求min{b, a+c}。将上面的三个不等式按照数形结合 中提到的方式建图,如上图所示
我们发现min{b, a+c}正好对应了A到C的最短路,而这三个不等式就是著名的三角不等式。将三个不等式推广到m个,变量推广到n个,就变成了n个点m条边的最短路问题了.
3、解的存在性
上文提到最短路的时候,会出现负权圈或者根本就不可达的情况,所以在不等式组转化的图上也有可能出现上述情况,先来看负权圈的情况,如图三-3-1,下图为5个变量5个不等式转化后的图,需要求得是X[t] - X[s]的最大值,可以转化成求s到t的最短路,但是路径中出现负权圈,则表示最短路无限小,即不存在最短路,那么在不等式上的表现即X[t] - X[s] <= T中的T无限小,得出的结论就是 X[t] - X[s]的最大值 不存在, 如下图所示.
再来看另一种情况,即从起点s无法到达t的情况,如图三-3-2,表明X[t]和X[s]之间并没有约束关系,这种情况下X[t] - X[s]的最大值是无限大,这就表明了X[t]和X[s]的取值有无限多种, 如下图所示.
在实际问题中这两种情况会让你给出不同的输出。综上所述,差分约束系统的解有三种情况:1、有解;2、无解;3、无限多解;
4、最大值 => 最小值
然后,我们将问题进行一个简单的转化,将原先的”<=”变成”>=”,转化后的不等式如下:
B - A >= c (1)
C - B >= a (2)
C - A >= b (3)
然后求C - A的最小值,类比之前的方法,需要求的其实是max{b, c+a},于是对应的是上面那个三角不等式就是从A到C的最长路。同样可以推广到n个变量m个不等式的情况.
5、不等式标准化
如果给出的不等式有”<=”也有”>=”,又该如何解决呢?很明显,首先需要关注最后的问题是什么,如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成”<=”的形式,建图后求最短路;相反,如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成”>=”,建图后求最长路. (对问题的建图方式)
如果有形如:A - B = c 这样的等式呢?我们可以将它转化成以下两个不等式:
A - B >= c (1)
A - B <= c (2)
再通过上面的方法将其中一种不等号反向,建图即可。
最后,如果这些变量都是整数域上的,那么遇到A - B < c这样的不带等号的不等式,我们需要将它转化成”<=”或者”>=”的形式,即 A - B <= c - 1.
剩下的就是全靠做题积累了.