zoukankan      html  css  js  c++  java
  • 差分约束小结

    ZOJ 2770 Burn the Linked Camp

    /*
        ZOJ 2770 Burn the Linked Camp
        差分约束
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    const int MAXN = 1009;
    
    struct Edge {
        int v, ne, c;
    } G[MAXN*MAXN];
    int head[MAXN], cnt;
    
    int C[MAXN], S[MAXN], dis[MAXN];
    int vis[MAXN], sum[MAXN];
    
    int n, m;
    
    inline void addE (int u, int v, int c) {
        G[++cnt].v = v, G[cnt].c = c, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    
    inline int SPFA() {
        queue<int> Q;
        dis[n] = 0, vis[n] = 1;
        Q.push (n);
        while (!Q.empty() ) {
            int k = Q.front(); Q.pop();
            for (int i = head[k]; i; i = G[i].ne) {
                int v = G[i].v;
                if (dis[k] + G[i].c < dis[v]) {
                    dis[v] = dis[k] + G[i].c;
                    if (!vis[v]) Q.push (v), vis[v] = 1, sum[v]++;
                    if (sum[v] >= n) return -1;
                }
            }
            vis[k] = 0;
        }
        return dis[n]-dis[0];
    }
    
    inline void init() {
        memset (head, 0, sizeof head);
        memset (sum, 0, sizeof sum);
        memset (vis, 0, sizeof vis);
        memset (dis, 1, sizeof dis);
        cnt = 0;
    }
    
    int main() {
        while (scanf ("%d %d", &n, &m) == 2) {
            init();
            for (int i = 1; i <= n; i++) {
                scanf ("%d", &C[i]);
                S[i] = S[i - 1] + C[i];
                addE (i, i - 1, 0);
            }
            for (int i = 1; i <= n; i++)
                for (int j = i; j <= n; j++)
                    addE (i - 1, j, S[j] - S[i - 1]);
            for (int i = 1, l, r, num; i <= m; i++) {
                scanf ("%d %d %d", &l, &r, &num);
                addE (r, l - 1, -num);
            }
            int ans = SPFA();
            if (ans != -1) printf ("%d
    ", ans);
            else puts ("Bad Estimations");
        }
    }
    ZOJ 2770

    ZOJ 1508 Intervals

    /*
        ZOJ 1508 Intervals
        差分约束
    */
    #include <iostream>
    #include <cstring>
    #include <queue>
    #include <cstdio>
    using namespace std;
    
    const int MAXN = 500009;
    struct edge {
        int v, ne, c;
    } G[MAXN << 1];
    int head[MAXN], cnt;
    
    int dis[MAXN], vis[MAXN];
    int n, m, mr, ml;
    
    inline void addE (int u, int v, int c) {
        G[++cnt].v = v, G[cnt].c = c, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    inline int SPFA() {
        queue<int> ql;
        ql.push (mr);
        vis[mr] = 1, dis[mr] = 0;
        while (!ql.empty() ) {
            int x = ql.front(); ql.pop();
            for (int i = head[x]; i; i = G[i].ne) {
                int v = G[i].v;
                if (dis[x] + G[i].c < dis[v]) {
                    dis[v] = dis[x] + G[i].c;
                    if (!vis[v]) vis[v] = 1, ql.push (v);
                }
            }
            vis[x] = 0;
        }
        return -dis[ml - 1];
    }
    inline void init() {
        memset (head, 0, sizeof head);
        memset (dis, 1, sizeof dis);
        mr = 0, ml = 0x7fffffff;
        cnt = 0;
    }
    
    int main() {
        while (scanf ("%d", &n) == 1) {
            init();
            for (int i = 1, l, r, num; i <= n; i++) {
                scanf ("%d %d %d", &l, &r, &num);
                addE (r, l - 1, -num);
                mr = max (mr, r), ml = min (ml, l);
            }
            for (int i = ml; i <= mr; i++)
                addE (i - 1, i, 1), addE (i, i - 1, 0);
            printf ("%d
    ", SPFA() );
        }
    }
    ZOJ 1508

    ZOJ 1260 King

    /*
        ZOJ 1060 King
        差分约束
    */
    #include <iostream>
    #include <cstring>
    #include <queue>
    #include <cstdio>
    using namespace std;
    const int MAXN = 1009;
    
    struct edge {
        int v, c, ne;
    } G[MAXN*MAXN];
    int head[MAXN], cnt;
    int dis[MAXN], vis[MAXN], sum[MAXN];
    
    int n, m;
    
    inline void addE (int u, int v, int c) {
        G[++cnt].v = v, G[cnt].c = c, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    
    inline void init() {
        memset (head, 0, sizeof head);
        memset (dis, 1, sizeof dis);
        memset (vis, 0, sizeof vis);
        memset (sum, 0, sizeof sum);
        cnt = 0;
    }
    
    inline bool SPFA() {
        queue<int> ql;
        dis[n] = 0;
        for(int i=1;i<=n;i++){
            ql.push(i),vis[i]=1;
        }
        while (!ql.empty() ) {
            int x = ql.front(); ql.pop();
            for (int i = head[x]; i; i = G[i].ne) {
                int v = G[i].v;
                if (dis[x] + G[i].c < dis[v]) {
                    dis[v] = dis[x] + G[i].c;
                    if (!vis[v]) vis[v] = 1, ql.push (v), sum[v]++;
                    if (sum[v] > n) return 0;
                }
            }
            vis[x] = 0;
        }
        return 1;
    }
    int main() {
        char s[10];
        while (scanf ("%d", &n) == 1 && n) {
            scanf ("%d", &m);
            init();
            for (int i = 1, l, r, c; i <= m; i++) {
                scanf ("%d %d %s %d", &l, &r, s, &c);
                r = l + r;
                if (s[0] == 'g')     addE (r, l-1, -c - 1);
                else                addE (l-1, r, c - 1);
            }
            if (SPFA() ) puts ("lamentable kingdom");
            else puts ("successful conspiracy");
        }
    }
    ZOJ 1260

    以上三题都是根据题意直接建图的基础题。 


    ZOJ 1420 Cashier Employment

    /*
        ZOJ 1420 Cashier Employment
        差分约束
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    const int MAXN = 1009;
    struct edge {
        int v, c, ne;
    } G[MAXN<<4];
    int head[MAXN], cnt;
    int dis[MAXN], vis[MAXN], sum[MAXN];
    
    int need[MAXN], join[MAXN], s[MAXN];
    int  n, m;
    
    inline void addE (int u, int v, int c) {
        G[++cnt].v = v, G[cnt].c = c, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    
    inline void init() {
        memset (head, 0, sizeof head);
        memset (sum, 0, sizeof sum);
        memset (vis, 0, sizeof vis);
        memset (dis, 1, sizeof dis);
        cnt = 0;
    }
    
    inline bool SPFA() {
        queue<int> ql;
        dis[24] = 0;
        for (int i = 0; i <= 24; i++)
            ql.push (i), vis[i] = 1;
        while (!ql.empty() ) {
            int x = ql.front(); ql.pop();
            for (int i = head[x]; i; i = G[i].ne) {
                int v = G[i].v;
                if (dis[x] + G[i].c < dis[v]) {
                    dis[v] = dis[x] + G[i].c;
                    if (!vis[v]) vis[v] = 1, ql.push (v), sum[v]++;
                    if (sum[v] >= 23) return 0;
                }
            }
            vis[x]=0;
        }
        return 1;
    }
    
    inline bool make (int ans) {
        init();
        addE (24, 0, -ans);
        for (int i = 1; i <= 24; i++) {
            addE (i, i - 1, 0);
            addE ( i - 1, i, join[i]);
        }
        for (int i = 9; i <= 24; i++)    addE (i, i - 8, -need[i]);
        for (int i = 1; i <= 8; i++) addE (i, i + 16, ans - need[i]);
        return SPFA();
    }
    
    int main() {
        int Case;
        scanf ("%d", &Case);
        while (Case--) {
            for (int i = 1; i <= 24; i++) scanf ("%d", &need[i]);
            scanf ("%d", &n);
            for (int i = 1, x; i <= n; i++) {
                scanf ("%d", &x);
                join[x+1]++;
            }
            int l = 0, r = n, last = -1;
            while (l <= r) {
                int mid = (l + r) >> 1;
                if (make (mid) )
                    last = mid, r = mid - 1;
                else
                    l = mid + 1;
            }
            if (last != -1) printf ("%d
    ", last);
            else    puts ("No Solution");
        }
    }
    ZOJ 1420

     这题考虑

      令 need[i] 为第i时刻需要多少出纳员,

              join[i]为第i时刻有多少人应聘

              s[i]作为节点,同时代表着从0时刻到i时刻需要多少出纳员

        二分需要的出纳员数ans

        可以列出关系:

        s[i]-s[i-1]>=0;

      s[i]-s[i-1]<=join[i];

      s[24]-f[0]<=ans;

      s[i]-s[i-8]>=need,i>8;

      s[i]+s[24]-f[i+16]>=need[i],i<=8;


    ZOJ 1455 Schedule Problem

    关键在于使图中所有结点连通。添加一个虚拟的任务0,要求在所有任务开始后开始。这时令0号任务的开时间为0,找到其它点的最短路径。

    s[i]为任务i的开始时间,len[i]为任务I的持续时间

    关系式

        FAS u v     s[u]+len[u]>=s[v];

      FAF u v     s[u]+len[u]>=s[v]+len[v];

      SAF u v     s[u]>=s[v]+len[v];

        SAF u v     s[u]>=s[v];

                        s[0]>=s[i]+len[i];

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    using namespace std;
    
    const int MAXN = 10009;
    struct edge {
        int v, c, ne;
    } G[MAXN << 4];
    int head[MAXN], cnt;
    int sum[MAXN], vis[MAXN], dis[MAXN];
    
    int n;
    int  Len[MAXN];
    
    inline void addE (int u, int v, int c) {
        G[++cnt].v = v, G[cnt].c = c, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    
    inline void init() {
        memset (head, 0, sizeof head);
        memset (sum, 0, sizeof sum);
        memset (vis, 0, sizeof vis);
        memset (dis, 1, sizeof dis);
        cnt = 0;
    }
    inline bool SPFA() {
        queue<int> ql;
        dis[0] = 0, vis[0] = 1, ql.push (0);
        while (!ql.empty() ) {
            int x = ql.front(); ql.pop();
            for (int i = head[x]; i; i = G[i].ne) {
                int v = G[i].v;
                if (dis[x] + G[i].c < dis[v]) {
                    dis[v] = dis[x] + G[i].c;
                    if (!vis[v]) vis[v] = 1, ql.push (v), sum[v]++;
                    if (sum[v] >= n) return 0;
                }
            }
            vis[x] = 0;
        }
        return 1;
    }
    int main() {
        char s[10];
        int Cs = 0;
        while (scanf ("%d", &n) == 1 && n) {
            init();
            for (int i = 1; i <= n; i++) {
                    scanf ("%d", &Len[i]);
                    addE (0, i, -Len[i]);
            }
            int u, v;
            while (scanf ("%s", s) == 1 && s[0] != '#') {
                scanf ("%d %d", &u, &v);
                if (!strcmp (s, "FAS") ) addE (u, v, Len[u]);
                if (!strcmp (s, "FAF") ) addE (u, v, Len[u] - Len[v]);
                if (!strcmp (s, "SAF") ) addE (u, v, -Len[v]);
                if (!strcmp (s, "SAS") ) addE (u, v, 0);
            }
            printf ("Case %d:
    ", ++Cs);
            if (SPFA() ) {
                int t=~(1<<31);
                for (int i = 1; i <= n; i++) t=min(t,dis[i]);
                for (int i = 1; i <= n; i++)
                    printf ("%d %d
    ", i, dis[i]-t);
            }
            else puts ("impossible");
            puts("");
        }
    }
    ZOJ 1455

    POJ 3169 Layout

        同样是简单的建图

         令s[i]为第i头牛的位置:

          有关系:

           ML u v l    s[v]-s[u]<=l;

           MD u v l   s[v]-s[u]>=l;

           还有         s[i]-s[i-1]>=0;

    /*
        POJ 3169 Layout
        差分约束
    */
    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    
    const int MAXN = 1009;
    struct edge {
        int v, c, ne;
    } G[MAXN*MAXN];
    int head[MAXN], cnt;
    int sum[MAXN], vis[MAXN], dis[MAXN];
    
    int n, ML, MD;
    
    inline void addE (int u, int v, int c) {
        G[++cnt].v = v, G[cnt].c = c, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    
    inline void init() {
        memset (vis, 0, sizeof vis);
        memset (head, 0, sizeof head);
        memset (dis, 1, sizeof dis);
        memset (sum, 0, sizeof sum);
        cnt = 0;
    }
    
    inline int SPFA() {
        queue<int> ql;
        dis[1] = 0, vis[1] = 1;
        ql.push (1);
        while (!ql.empty() ) {
            int x = ql.front(); ql.pop();
            for (int i = head[x]; i; i = G[i].ne) {
                int v = G[i].v;
                if (dis[x] + G[i].c < dis[v]) {
                    dis[v] = dis[x] + G[i].c;
                    if (!vis[v]) vis[v] = 1, ql.push (v), sum[v]++;
                    if (sum[v] >= n) return -1;
                }
            }
            vis[x] = 0;
        }
        if (dis[n] == 0x01010101) return -2;
        return dis[n];
    }
    
    int main() {
        int u, v, c;
        init();
        scanf ("%d %d %d", &n, &ML, &MD);
        for (int i = 1; i <= ML; ++i) {
            scanf ("%d %d %d", &u, &v, &c);
            addE (u, v, c);
        }
        for (int i = 1; i <= MD; ++i) {
            scanf ("%d %d %d", &u, &v, &c);
            addE (v, u, -c);
        }
        for (int i = 2; i <= n; i++)
            addE (i, i - 1, 0);
        printf ("%d
    ", SPFA() );
    }
    POJ 3169

    UVA 11478 Halum

    题目说的也不是非常清楚,其实是求最小的非负边的最大值.

    跟前面的题相比这道题的约束条件看起来就不那么明显了

    先二分最短边为ans

    令 s[i] 为点i改变的数

    对每一条边u v c有   s[u]-s[v]>=ans-c;

    对于用这样的关系建出的图,如果存在负环就是无解.

    如果ans=最大边的情况下还没出现负环,那么最大值可以无限大

    同理 ans=1的时候就有负环了,就是不能满足条件.

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    
    const int MAXN = 600;
    struct edge {
        int v, c, ne;
    } G[MAXN*MAXN];
    int head[MAXN], cnt;
    int vis[MAXN], sum[MAXN], dis[MAXN];
    
    int n, m;
    
    inline void add (int u, int v, int d) {
        G[++cnt].v = v, G[cnt].c = d, G[cnt].ne = head[u];
        head[u] = cnt;
    }
    
    inline bool SPFA() {
        memset (dis, 0, sizeof dis);
        queue<int> q;
        for (int i = 1; i <= n; i++)
            vis[i] = 1, sum[i] = 1,    q.push (i);
        while (!q.empty() ) {
            int u = q.front(); q.pop();
            vis[u] = 0;
            for (int i = head[u]; i != 0; i = G[i].ne) {
                int v = G[i].v;
                if (dis[u] + G[i].c < dis[v]) {
                    dis[v] = dis[u] + G[i].c;
                    if (!vis[v]) q.push (v), vis[v] = 1;
                    if (++sum[v] >= n)        return 0;
                }
            }
        }
        return true;
    }
    
    inline bool make (int x) {
        for (int i = 1; i <= cnt; i++)     G[i].c -= x;
        bool ok = SPFA();
        for (int i = 1; i <= cnt; i++)    G[i].c += x;
        return ok;
    }
    
    int  main() {
        while (~scanf ("%d%d", &n, &m) ) {
            memset (head, 0, sizeof (head) );
            cnt = 0;
            int u, v, x;
            for (int i = 0; i < m; i++) {
                scanf ("%d%d%d", &u, &v, &x);
                add (u, v, x);
            }
            if (make (10009) ) puts ("Infinite");
            else if (!make (1) ) puts ("No Solution");
            else {
                int l = 1, r = 10009, ans = 1;
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (make (mid) )
                        ans = mid, l = mid + 1;
                    else
                        r = mid - 1;
                }
                printf ("%d
    ", ans);
            }
        }
    }
    uva 11478

    小结:

          总的来说,这类题需要先找到题目中的约束条件,一般来说有求最值,判断可行性这几种问题。

          对于求最值的情况,根据图的定义如果是直接可以通过路径长度得到的话,需要注意起点的设计,最短路还是最长路。如果是需要通过枚举或者二分求的最值,需要注意可行性需要判断的是负环还是正环。

          有时候需要添加额外的点,使整个图连通。或者根据求的路径的性质(最长,最短)赋好距离初值,然后只要将所有点加入队列就行。

  • 相关阅读:
    s3c2440——按键中断
    s3c2440——swi异常
    S3C2440的七种模式之——未定义模式(去掉bl print1 bug解决)
    Entity Framework DBContext 增删改查深度解析
    Git换行符是如何精确控制的
    Entity Framework 乐观并发处理
    Session, Token and SSO 有什么区别
    2017让我受益最大的那些书--别找了,都在这里
    GDB scheduler-locking 命令详解
    Java web servers 间是如何实现 session 同步的
  • 原文地址:https://www.cnblogs.com/keam37/p/4361047.html
Copyright © 2011-2022 走看看