差分约束系统学习笔记

在这里插入图片描述

差分约束系统

差分约束系统是最短路的一类经典应用。如果一个不等式组由n个变量和m个约束条件组成,且每个约束条件都是形如 i-j≤k,1≤i≤n,1≤j≤n的不等式,则称其为 差分约束系统 (system of differenceconstraints)。差分约束系统是求解一组变量的不等式组的算法。

问题转化


我们在求解差分约束系统时,可以将其转化为图论中单源最短路(或最长路)问题。
对于不等式中的其中一组j−i≤k,我们会发现它类似最短路网络(全部由最短路上的边组成的子图)中的三角不等式d[v]−d[u]≤w<u,v>,即x[u]+w<u,v>≥x[v],所以我们可以理解成从顶点d[u]到顶点d[v]连一条权值为w<u,v>的边,用最短路算法得到最短路的答案x[i],也就求出了原不等式组的一个解。
因此我们可以将每个变量i作为一个顶点,对于约束条件x[j]-x[i]≤k,连接一条边权为k的有向边<i,j>。我们再增加一个超级源s,s连向其余每个顶点,边权均为0。如果用x[i]表示s到i的最短路, 用x[j]表示s到j的最短路,在求最短路的时候,由于边<i,j>的存在,那么一定有x[j]≤x[i]+k,自然就满足了我们的不等式了。
如果程序正常结束,那么得到的最短路答案数组d[i]就是满足条件的一组x[i]的解。
若图中存在负环,则该不等式组无解。比如存在如下一个负环

在这里插入图片描述
在这里插入图片描述
对应的不等式组为:
x[2]≤x[1]−3
x[3]≤x[2]−4
x[1]≤x[3]+4
经过替换,最终得到[1]≤x[1]−3,这是不可能成立的。所以一个负环实际上对应了一组矛盾的不等式。

在这里插入图片描述
对于下面的不等式组,我们建出的图如下。
x

两种连边方法


第一种是连边后求最短路的方法,对于x[j]−x[i]≤k,变形为x[j]≤x[i]+k,从i到j连一条权值为k的边。若加入超级源点,最后求出最短路,实际上表示在x[i]≤0 的情况下,所有x的 最大的解。若存在负环,就无解。
第二种是连边后求最长路的方法,对于x[j]−x[i]≤k,变形为x[i]≥x[j]−k,从j到i连一条权值为-k的边。若加入超级源点,最后求最长路,实际上表示在x[i]≥0 的情况下,所有x的 最小的解。若存在正环,就无解。

一道题

第一行有两个数字n,m分别表示有n个变量m个表达式。
接下来会有m行,每行有四个数字。第一个数字表示是这个表达式是大于等于,还是小于等于,或者是等于。后面的三个数字就对应上面表达式中的数字。
1:≤
2:≥
3:=

计算最小值就是计算最长路问题

直接上代码

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
struct edge {
    int v, w, fail;
    edge() {}
    edge(int _v, int _w, int _fail) {
        v = _v;
        w = _w;
        fail = _fail;
    }
} e[M << 1];
int head[N], len;
void init() {
    memset(head, -1, sizeof(head));
    len = 0;
}
void add(int u, int v, int w) {
    e[len] = edge(v, w, head[u]);
    head[u] = len++;
}
void add2(int u, int v, int w) {
    add(u, v, w);
    add(v, u, w);
}
int n, m;
int dis[N], in[N];
bool vis[N];
bool spfa(int u) {
    memset(vis, false, sizeof(vis));
    vis[u] = true;
    memset(dis, -1, sizeof(dis));
    dis[u] = 0;
    memset(in, 0, sizeof in);
    in[u] = 1;
    queue<int> q;
    q.push(u);
    while (!q.empty()) {
        u = q.front();
        q.pop();
        vis[u] = false;
        for (int j = head[u]; ~j; j = e[j].fail) {
            int v = e[j].v;
            int w = e[j].w;
            if (dis[v] < dis[u] + w) { // 求最长路,和求最短路相反
                dis[v] = dis[u] + w;
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = true;
                    ++in[v];
                    if (in[v] > n + 1) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

int main() {
    init();
    int u, v, w, op;
    cin >> n >> m;
    while (m--) {
        cin >> op;
        cin >> u >> v >> w;
        if (op == 1) {
            add(u, v, -w);
        }
        else if (op == 2) {
            add(v, u, w);
        }
        else {
            add(u, v, -w);
            add(v, u, w);
        }
    }
    for (int i = 1; i <= n; ++i) {
        add(0, i, 0);
    }
    if (spfa(0)) {
        cout << "no" << endl;
    } else {
        for (int i = 1; i <= n; ++i) {
            cout << "x" << i << " = " << dis[i] << endl;
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值