• 差分约束系统详解


    差分约束系统

    差分约束系统即给一些限制条件,问你满足条件的最优解(最大最小值),或者判断添加你是否成立。

    解的存在性

    差分约束系统中,有两种特殊情况,1种是出现负权圈时, 如下图,求(arr[t] - arr[s])的最大值, 这时 s 到 t 不存在最短路, 即最大值不纯在。

    还有一种情况是 s 和 t 没有约束关系,如下图所示。这时他们得最大值为正无穷最小值为负无穷。

    即解的情况有三种:有解, 无解, 无限多解。

    不等式标准化

    在求解差分约束系统的最优解时, 我们需要将不等式标准化。这时我们要根据题目中的要求做相应变换。如果题目要求最大值,我们则将不等式化作(a[i] - a[j] <= t),建一条 j --> i 的权值为t边, 并求最短路;反之要求最小值,我们则将不等式化作(a[i] - a[j] >= t), 建一条 j --> i 的权值为t的边, 并求最长路。

    还有需要建的几种特殊的边。如果题目约束 (a[i] - a[j] == t),那么我们可以将式子转化成下面两个式子:

    [a[i] - a[j] >= t\ a[i] - a[j] <= t ]

    再将其中一种不等号反向, 建图即可。

    同时如果题目要求(a[i] - a[j] < t), 那么在整数条件下我们可以将其改为(a[i] - a[j] <= t - 1),实数的话加个eps即可。

    ps:在判断有无负环时, 我们用dfs比较好, 因为用bfs可能会mle或tle。

    例题

    模板题

    【描述】:n 个未知数, m 个不等式形如(a - b >= c), 有解则输出一组解, 无解则输出NO。

    【思路】:直接差分约束, 用一下超级源点的思想从0给所有未知数加个权值为0的边, 最后泡一下spfa(0),有解则dis[i]就是满足要求的每个未知数的解。

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int maxn = 4e5 + 10;
    const int inf = 0x3f3f3f3f;
    int gcd(int a,int b)    { return b == 0 ? a : gcd(b, a % b); }
    
    struct farm{
        int nex, to, val;
    }Edge[maxn<<1];
    int head[maxn], n, m, tot;
    queue<int> q;
    int vis[maxn], dis[maxn], mk[maxn];
    
    inline void init(){
        tot = 0;
        memset(head, -1, sizeof(head));
    }
    inline void add(int from, int to, int val){
        Edge[++tot].to = to;
        Edge[tot].val = val;
        Edge[tot].nex = head[from];
        head[from] = tot;
    }
    bool spfa(int s)
    {
        for(int i = 1; i <= n; ++i){
            mk[i] = vis[i] = 0;
            dis[i] = i == s ? 0 : inf;
        }
        q.push(s);
        vis[s] = 1; mk[s]++;
        while(!q.empty()){
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int i = head[u]; i != -1; i = Edge[i].nex){
                int v = Edge[i].to;
                if(dis[v] > dis[u] + Edge[i].val){
                    dis[v] = dis[u] + Edge[i].val;
                    if(vis[v])  continue;
                    vis[v] = 1;
                    if(++mk[v] >= n)    return 0;
                    q.push(v);
                }
            }
        }
        return 1;
    }
    
    int main()
    {
        scanf("%d %d", &n, &m);
        init();
        for(int i = 1; i <= m; ++i){
            int a, b, val;
            scanf("%d %d %d", &a, &b, &val);
            add(b, a, val);
        }
        for(int i = 0; i <= n; ++i){
            add(0, i, 0);
        }
        int ok = spfa(0);
        if(!ok) puts("NO");
        else{
            for(int i = 1; i <= n; ++i){
                printf("%d%c", dis[i], i == n ? '
    ' : ' ');
            }
        }
        system("pause");
    }
    

    P1250种树

    【描述】:n 个区域编号 1...n,m 个房子,每个房子都有一个要求:在s ~ t 区域内至少种 val 棵树, 问最少种即可树满足要求?

    【思路】:这题求得是最小值, 我们可以将不等式化成 ">=" 的形式, 用前缀和来表示, a - b中至少有val棵树, 即:(sum[a] - sum[b-1] >= val), 故可建 (b-1) --> a 的权值为 val 的边, 同时还有一个约束条件:(1 >= sum[i] - sum[i-1] >= 0),即:(sum[i] - sum[i-1] >= 0), (sum[i-1] - sum[i] >= -1);即建(i - 1) --> i的权值为0的边和i --> (i-1)的权值为-1的边, 最后spfa跑一边最长路, 输出dis[n]即可。

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int maxn = 2e5 + 10;
    const int inf = 0x3f3f3f3f;
    int gcd(int a,int b)    { return b == 0 ? a : gcd(b, a % b); }
    
    int n, m;
    int dis[maxn], vis[maxn], tot;
    struct edge{
        int nex, to, val;
    }Edge[maxn];
    int head[maxn];
    
    inline void add(int from, int to, int val){
        Edge[++tot].to = to;
        Edge[tot].val = val;
        Edge[tot].nex = head[from];
        head[from] = tot;
    }
    void spfa(int s)
    {
        for(int i = 0; i <= n; ++i){
            dis[i] = -inf;
            vis[i] = 0;
        }
        queue<int> q;
        q.push(s);
        dis[s] = 0; vis[s] = 1;
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int i = head[u]; i != -1; i = Edge[i].nex){
                int v = Edge[i].to;
                if(dis[v] < dis[u] + Edge[i].val){
                    dis[v] = dis[u] + Edge[i].val;
                    if(vis[v])  continue;
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    
    int main()
    {
        scanf("%d %d", &n, &m);
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; ++i){
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            add(a-1, b, c);
        }
        for(int i = 1; i <= n; ++i){
            add(i - 1, i, 0);
            add(i, i - 1, -1);
        }
        spfa(0);
        printf("%d
    ", dis[n]);
        system("pause");
    }
    

    P1993 小K的农场

    【描述】:n 个农场, m个不等式, 1开头表示 a 比 b 至少多 val 个, 2开头表示a 比 b 至多多多少个, 3开头表示 a 和 b 相等, 问有没有解。n, m 都是 1e4 范围。

    【思路】:差分约束判断负环即可。spfa判断负环时间复杂度为(O(nm)), 在这可能会超时, 所以用dfs模拟一下即可。

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int maxn = 2e5 + 10;
    const int inf = 0x3f3f3f3f;
    int gcd(int a,int b)    { return b == 0 ? a : gcd(b, a % b); }
    
    struct edge{
        int nex, to, val;
    }Edge[maxn];
    int head[maxn], tot, n, m;
    int dis[maxn], mk[maxn], vis[maxn];
    
    inline void add(int from, int to, int val){
        Edge[++tot].to = to;
        Edge[tot].val = val;
        Edge[tot].nex = head[from];
        head[from] = tot;
    }
    bool spfa(int u){
        vis[u] = 1;
        for(int i = head[u]; i != -1; i = Edge[i].nex){
            int v = Edge[i].to;
            if(dis[v] < dis[u] + Edge[i].val){
                dis[v] = dis[u] + Edge[i].val;
                if(vis[v])  return 0;
                if(!spfa(v)) return 0;
            }
        }
        vis[u] = 0;
        return 1;
    }
    
    int main()
    {
        scanf("%d %d", &n, &m);
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; ++i){
            int a, b, val, f;
            scanf("%d %d %d", &f, &a, &b);
            if(f == 1){
                scanf("%d", &val);
                add(b, a, val);
            }
            else if(f == 2){
                scanf("%d", &val);
                add(a, b, -val);
            }
            else{
                add(b, a, 0);
                add(a, b, 0);
            }
        }
        for(int i = 1; i <= n; ++i){
            add(0, i, 0);
            dis[i] = -inf;
        }
        if(spfa(0)) puts("Yes");
        else puts("No");
        system("pause");
    }
    
  • 相关阅读:
    解决ASP.NET中的各种乱码问题
    window.open不被拦截的实现代码
    window.open 浏览器差异.
    使用jQuery获取radio/checkbox组的值的代码收集
    Libnids(Library Network Intrusion Detection System) .
    基于libnids的TCP数据流的还原(多线程实现) .
    Libnids读书笔记 (转)
    PPPOE数据包转换及SharpPcap应用
    [Asp.net]常见word,excel,ppt,pdf在线预览方案,有图有真相,总有一款适合你!
    asp.net 读取word 文档的方法
  • 原文地址:https://www.cnblogs.com/StungYep/p/12345611.html
Copyright © 2020-2023  润新知