• 浅谈差分约束系统


    1.从代数到图论

    1.1.差分约束方程

    差分约束方程就是形如 (x_i-x_j leq w_{ij})(x_i-x_j geq w_{ij})方程组

    比如说:

    (egin{cases}x_1-x_3 leq 5 \x_1-x_2 leq 2 \x_2-x_1 leq 0 \x_2-x_3 leq 2 \x_3-x_2 leq -1 \ x_3-x_1 leq -2 end{cases})

    就是一组差分方程。
    下面是它的一组解:

    (egin{cases}x_1=5 \x_2=3 \x_3=1 end{cases})

    一组差分约束方程要么有无穷组解,要么无解。因为如果它有解,那么它的解同时加上一个实数 (k),依旧是一组解。因为每个数都加(k),他们任意两个数之间的差是不变的,所以对于不等式没有影响。


    (mathtt{For} mathtt{Example}:)

    上式的解还可以是
    (egin{cases}x_1=6 \x_2=4 \x_3=2 end{cases})
    (同时加(1)


    1.2.图论解法

    多数博客接下来是这么说的:

    看到 (x_i - x_j leq w_{ij})
    有没有想到什么呢? 可以变形为 (x_i leq x_j + w_{ij}) ,这与单元最短路中的三角形不等式(D_i<=D_j+w_{ij})非常相似
    因此,可以把每个变量 (x_i) 看做有向图中的一个结点(i),对于约束条件 (X_i - X_j ≤ w_{ij}) ,从结点(j)向结点i连一条权值为 (w_{ij}) 的有向边。

    这里补充一下:(D_i)就是上面(x_i)的一组解。

    讲得是没错,然而,凭什么长得像就可以随便乱套呢?

    首先第一步最开始,
    我们画张图看看:

    e8SIDP.png

    比如对于(x_1-x_3 leq 5),如果有当前从1到3的最短路大于5,那么一定会被这条路((1 ightarrow3))替换掉。

    同理,对于 (x_i-x_j leq w_{ij}),如果有当前从(i)(j)的最短路大于(w_{ij}),那么一定会被这条路((i ightarrow j))替换掉。

    大于也是同样的原理,可以举一反三。

    同理可得其它边,以保证在前提(差分约束方程)不被违反的情况下得到最优解。

    2.差分约束系统

    2.1.大于还是小于

    众所周知,不等式方程是互通的,它们可以互相转换。


    (mathtt{For} mathtt{Example}:)

    刚才的方程组:

    (egin{cases}x_1-x_3 leq 5 \x_1-x_2 leq 2 \x_2-x_1 leq 0 \x_2-x_3 leq 2 \x_3-x_2 leq -1 \ x_3-x_1 leq -2 end{cases})

    等价于:

    (egin{cases}x_3-x_1 geq -5 \x_2-x_1 geq -2 \x_1-x_2 geq 0 \x_3-x_2 geq -2 \x_2-x_3 geq 1 \ x_1-x_3 geq 2 end{cases})


    那么差分约束系统该用大于还是小于呢?

    事实上,两者都有使用,具体看情况。

    小于求最短路得到最大值,

    大于求最长路得到最小值。

    2.2.无解情况

    并不是所有差分约束方程都有解。大致可分为两类:

    2.2.1.条件矛盾

    顾名思义,满足了条件A就无法满足条件B。


    (mathtt{For} mathtt{Example}:)

    (egin{cases}x_1-x_2 leq 5 \ x_2-x_1 leq -6end{cases})

    等价于:

    (egin{cases}x_1-x_2leq5 \ x_1-x_2geq6end{cases})


    2.2.2.无关未知数

    这个其实不一定算无解,不过和2.2.1判断方法一致,就勉强算无解吧。
    未知数没有构成强连通分量。


    (mathtt{For} mathtt{Example}:)

    (egin{cases}x_1-x_2 leq 5 \ x_3-x_1 leq 2\ x_4-x_5 leq 2 \ x_5-x_6 leq -3end{cases})

    这里
    (x_1,x_2,x_3)(x_4,x_5,x_6)毫无关联。


    2.2.3.判断无解

    最短路有负环或最长路有正环即为无解。

    2.3.超级原点

    超级原点连接到所有点,且权值都是0。

    在求最长路时,可以加入一个超级原点以简化代码。

    最短路似乎好像也许可能可以加入超级原点,不过容易引起错误。

    2.4.去除重边

    重复的边会容易导致错误(误判负环)
    去重边的伪代码

    flag <- 1
    for i in G[u]
        if G[u,i].v = v
            if w better than G[u,i].w
                set new G[u,i].w
            flag <- 0;
    
    if flag = 1
        G[u].append(v, w)
    

    3.模板代码

    就是一个spfa模板:

    bool spfa() {
        memset(d, 0xef, sizeof(d));
        d[0] = 0;
        q.push(0);
        tx[0] = 1;
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(unsigned i = 0; i < G[u].size(); ++i) {
                int v = G[u][i].v;
                int dis = G[u][i].w;
                if (d[v] > d[u] + dis) {
                    d[v] = d[u] + dis;
                    if (++tx[v] >= 40) {
                        return 0;
                    }
                    if (!vis[v]) {
                    	q.push(v);
                    	vis[v] = 1;
    				}
                }
            }
        }
    	return 1;
    }
    

    最长时改一下符号:

    if (dis[v] < dis[u] + w) {
    	--snip--
    }
    

    4.例题

    P1993 小K的农场

    模板题,长短路均可,主要问题是如何插入边:

    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; ++i) {
        scanf("%d %d %d", &t, &a, &b);
        if (t == 3) {
            G[a].push_back(node(b, 0));
            G[b].push_back(node(a, 0));
        } else {
            scanf("%d", &c);
            if (t == 1) {
                G[a].push_back(node(b, c));
            } else {
                G[b].push_back(node(a, -c));
            }
        }
    }
    

    tyvj-p1277 关系运算图

    好像现在看不到题了:

    Description
    给出一有向图,图中每条边都被标上了关系运算符‘<’,‘>’,‘=’。现在要给图中每个顶点标上一个大于等于0,小于等于k的某个整数使所有边上的符号得到满足。若存在这样的k,则求最小的k,若任何k都无法满足则输出NO。
    例如下表中最小的k为2。
    结点1>结点2
    结点2>结点3
    结点2>结点4
    结点3=结点4
    如果存在这样的k,输出最小的k值;否则输出‘NO’。
    Input
    共二行,第一行有二个空格隔开的整数n和m。n表示G的结点个数,m表示G的边数,其中1<=n<=1000, 0<=m<=10000。全部结点用1到n标出,图中任何二点之间最多只有一条边,且不存在自环。
    第二行共有3m个用空格隔开的整数,第3i-2和第3i-1(1<=i<=m)个数表示第i条边的顶点。第3i个数表示第i条边上的符号,其值用集合{-1,0,1}中的数表示:-1表示‘<’, 0 表示‘=’, 1表示‘>’。
    Output
    仅一行,如无解则输出‘NO’;否则输出最小的k的值。
    Sample Input
    4 4
    1 2 -1 2 3 0 2 4 -1 3 4 -1
    Sample Output
    2

    模板题,最长路

    HDU3440 House Man

    最短路,如果最高楼在最矮楼左边就翻转高度数组。

    @2019-7-31 贵州铜仁镇远古镇

  • 相关阅读:
    Java实现 洛谷 P1049 装箱问题
    (Java实现) 洛谷 P1781 宇宙总统
    (Java实现) 洛谷 P1319 压缩技术
    (Java实现) 蓝桥杯 国赛 重复模式
    qt编写一个只能运行单个实例的程序,不用Windows API
    Chaos Software Google Sync v10.1.1.0 和Syncovery Pro
    C++中new和delete的背后( call edx 调用虚表内的第二个函数(析构函数))
    C++中实现回调机制的几种方式(一共三种方法,另加三种)
    如何将Icon转成Bitmap(对ICON的内部格式讲的比较清楚)
    深入解析控制器运行原理
  • 原文地址:https://www.cnblogs.com/szdytom/p/11622594.html
Copyright © 2020-2023  润新知