ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

图论专题-学习笔记:差分约束

2022-04-17 15:32:19  阅读:178  来源: 互联网

标签:图论 不等式 int 短路 笔记 leq 差分 式子 dis


目录

一些 Update

Update 2021/11/16:发现之前推的结论有严重错误,现已更正,如果有读者被误导,在此深表歉意。

1. 前言

差分约束是一种最短路算法,专门用来解决下面这类问题:

已知 \(n\) 个正整数 \(x_1,x_2,...,x_n\),与 \(m\) 个形如 \(x_i-x_j \leq k,k \in Z\) 的不等式,问是否存在一组解使得 \(x_1,...,x_n\) 满足上述不等式并求出任意一组解。

前置知识:SPFA。

2. 详解

例题:P5960 【模板】差分约束算法

初看本题,可能会毫无头绪:这跟最短路有什么关系?

但实际上,\(x_i-x_j \leq k\) 这个式子我们稍微做个变形:

\[x_i \leq x_j + k \]

对比一下在最短路中我们经常用到的式子:

\[dis_u \leq dis_j+val \]

你会发现这两个式子实际上长得非常像。

有的人会问了:我们一般的转移式子不是 \(dis_u \geq dis_j+val\) 吗?为什么是小于等于号而不是大于等于号?

理由非常简单,因为第 1 个式子要求必须满足,而第 2 个式子在求完最短路后也必须满足(否则不是最短路)。

而 \(dis_u \geq dis_j + val\) 实质上是表示此时还没有求出最短路,需要继续转移。

所以采用了小于等于号。

因此如果我们将 \(x_i\) 视作 \(dis_i\),\(k\) 视作 \(val\),那么我们就可以这样做:

首先对于每一个式子 \(x_i-x_j \leq k\),从 \(j\) 向 \(i\) 连一条边。

然后建立一个超级起点 0,0 向所有点连一条边权为 0 的边。

这么做是为了方便求最短路径。

需要注意的是,在不同问题中边权不一定为 0,需要具体问题具体分析。

然后求一遍最短路,此时求出来的所有 \(dis_i\) 应该是满足原题中给出的不等式的。

那么 \(\{dis\}\) 就是满足条件的一组解。

当然所有 \(dis_i\) 同时加上任意常数 \(k\) 也行,反正做差之后被消掉了。

那么什么时候无解呢?

无解的情况:图中出现负环,此时表明有若干个不等式不能同时成立。

特别提醒:不能使用 dijkstra 求差分约束类题目,因为有负权边。

代码:

/*
========= Plozia =========
    Author:Plozia
    Problem:P5960 【模板】差分约束算法
    Date:2021/4/29
========= Plozia =========
*/

#include <bits/stdc++.h>
using std::queue;

typedef long long LL;
const int MAXN = 5e3 + 10;
int n, m, Head[MAXN], cnt_Edge = 1, dis[MAXN], cnt[MAXN];
struct node { int to, val, Next; } Edge[MAXN << 1];
bool book[MAXN];

int read()
{
    int sum = 0, fh = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
    return sum * fh;
}
void add_Edge(int x, int y, int z) { ++cnt_Edge; Edge[cnt_Edge] = (node){y, z, Head[x]}; Head[x] = cnt_Edge; }

void SPFA()
{
    memset(dis, 0x3f, sizeof(dis));
    memset(cnt, 0, sizeof(cnt));
    memset(book, 0, sizeof(book));
    queue <int> q; q.push(0); dis[0] = 0; book[0] = 1;
    while (!q.empty())
    {
        int x = q.front(); q.pop(); book[x] = 0;
        for (int i = Head[x]; i; i = Edge[i].Next)
        {
            int u = Edge[i].to;
            if (dis[x] + Edge[i].val < dis[u])
            {
                dis[u] = dis[x] + Edge[i].val;
                if (!book[u])
                {
                    book[u] = 1; q.push(u); ++cnt[u];
                    if (cnt[u] > n) { printf("NO\n"); exit(0); }
                }
            }
        }
    }
}

int main()
{
    n = read(), m = read();
    for (int i = 1; i <= m; ++i)
    {
        int y = read(), x = read(), z = read();//注意这里 x 和 y 的顺序
        add_Edge(x, y, z);
    }
    for (int i = 1; i <= n; ++i) add_Edge(0, i, 0);
    SPFA();
    for (int i = 1; i <= n; ++i) printf("%d ", dis[i]);
    printf ("\n"); return 0;
}

3. 扩展

两个结论:

  • \(x_i-x_j \leq k \leftrightarrow x_i \leq x_j+k\),这个式子与最短路的三角形不等式 \(dis_i \leq dis_j+k\) 很相似,因此连边 \((j,i,k)\)。
    由于是最短路的三角形不等式,因此原图要求最短路。
  • \(x_i-x_j \leq k \leftrightarrow x_j \geq x_i+k\),这个式子与最长路的三角形不等式 \(dis_j \geq dis_i-k\) 很相似,因此连边 \((i,j,-k)\)。
    由于是最长路的三角形不等式,因此原图要求最长路。

其实上面的第一个结论就是前面步步推理的结论,而第二个结论则是类比第一个结论的推理过程,采用了最长路来解决问题。

这两种结论都是对的,但是在运用这两种结论的时候需要注意一点:边权在题目中必须有意义!

比如说 P3275 [SCOI2011]糖果 这道题,如果采用最长路则答案正确,但是如果采用最短路答案是错误的。

为什么?

比如说 \(z=2\) 的时候有 \(a-b\leq-1\),如果是采用最短路,那么需要连边 \((b,a,-1)\),但是糖果数量显然非负,因此边权都无意义,求出来的答案自然是错的。

4. 总结

结论如下:

  • \(x_i-x_j \leq k \leftrightarrow x_i \leq x_j+k\),这个式子与最短路的三角形不等式 \(dis_i \leq dis_j+k\) 很相似,因此连边 \((j,i,k)\)。
    由于是最短路的三角形不等式,因此原图要求最短路。
  • \(x_i-x_j \leq k \leftrightarrow x_j \geq x_i-k\),这个式子与最长路的三角形不等式 \(dis_j \geq dis_i+k\) 很相似,因此连边 \((i,j,-k)\)。
    由于是最长路的三角形不等式,因此原图要求最长路。

标签:图论,不等式,int,短路,笔记,leq,差分,式子,dis
来源: https://www.cnblogs.com/Plozia/p/16155840.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有