zoukankan      html  css  js  c++  java
  • HIT 2739 The Chinese Postman Problem

    HIT_2739

        这个题目实际上相当于问至少给原图补多长的边才能够成一个欧拉回路,只不过这些补的边只能是由原图的若干条边拼接成的。

        对于入度大于出度的点应该和出度大于入度的点相连,如果确定点a和点b要连接的话,那么补的边应该是a到b的最短路,只不过我们现在不知道究竟应该将哪些点连接起来。

        其实可以构造费用流来解决到底哪些点相连的问题,首先将点按入度大于出度的(不妨记作a类点)和出度大于入度的(不妨记作b类点)分成两组,至于入度和出度相等的点是可以忽略的。然后将源点和a类点连容量为入度-出度,费用为0的点,同样的将b类点和汇点连容量为出度-入度,费用为0的点,这样就通过容量保证了每个点最终会变成入度和出度相等的点。接下来考虑费用,将a类点和b类点连容量为INF,费用为a->b最短路的边,这样就保证了最终补全的边的和是最小的。最后做最小费用最大流并判断是否满流即可。

        当然,构成欧拉回路还有一个必要的条件就是原图必须连通,因此一开始可以并查集先判断一下原图是否连通。

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<stdlib.h>
    #define MAXD 210
    #define MAXM 20410
    #define INF 0x3f3f3f3f
    const int Q = 205;
    int N, M, first[MAXD], e, next[MAXM], v[MAXM], flow[MAXM], cost[MAXM];
    int S, T, q[MAXD], inq[MAXD], dis[MAXD], pre[MAXD], p[MAXD];
    int SUM, TOT, f[MAXD][MAXD], indgr[MAXD], outdgr[MAXD];
    int find(int x)
    {
        return p[x] == x ? x : (p[x] = find(p[x]));    
    }
    void init()
    {
        int i, x, y, z;
        scanf("%d%d", &N, &M);
        memset(f, 0x3f, sizeof(f));
        memset(indgr, 0, sizeof(indgr));
        memset(outdgr, 0, sizeof(outdgr));
        SUM = 0;
        for(i = 0; i < N; i ++) p[i] = i;
        for(i = 0; i < M; i ++)
        {
            scanf("%d%d%d", &x, &y, &z);
            ++ indgr[y], ++ outdgr[x], SUM += z, f[x][y] = std::min(f[x][y], z);
            p[find(x)] = find(y);
        }
    }
    int check()
    {
        for(int i = 1; i < N; i ++) if(find(i) != find(0)) return 0;
        return 1;    
    }
    void floyd()
    {
        int i, j, k;
        for(i = 0; i < N; i ++) f[i][i] = 0;
        for(k = 0; k < N; k ++)
            for(i = 0; i < N; i ++)
                for(j = 0; j < N; j ++)
                    f[i][j] = std::min(f[i][j], f[i][k] + f[k][j]);
    }
    void add(int x, int y, int f, int c)
    {
        v[e] = y, flow[e] = f, cost[e] = c;
        next[e] = first[x], first[x] = e ++;    
    }
    void build()
    {
        int i, j;
        S = N, T = N + 1, TOT = 0;
        memset(first, -1, sizeof(first[0]) * (T + 1)), e = 0;
        for(i = 0; i < N; i ++)
        {
            if(indgr[i] > outdgr[i])
            {
                TOT += indgr[i] - outdgr[i];
                add(S, i, indgr[i] - outdgr[i], 0), add(i, S, 0, 0);
                for(j = 0; j < N; j ++)
                    if(outdgr[j] > indgr[j] && f[i][j] != INF)
                        add(i, j, INF, f[i][j]), add(j, i, 0, -f[i][j]);    
            }
            else if(outdgr[i] > indgr[i])
                add(i, T, outdgr[i] - indgr[i], 0), add(T, i, 0, 0);
        }
    }
    int bfs()
    {
        int i, j, x, front = 0, rear = 0;
        memset(dis, 0x3f, sizeof(dis[0]) * (T + 1));
        dis[S] = 0, pre[S] = -1, q[rear ++] = S;
        memset(inq, 0, sizeof(inq[0]) * (T + 1));
        while(front != rear)
        {
            x = q[front ++], inq[x] = 0;
            front > Q ? front = 0 : 0;
            for(i = first[x]; i != -1; i = next[i])
                if(flow[i] && dis[x] + cost[i] < dis[v[i]])
                {
                    dis[v[i]] = dis[x] + cost[i], pre[v[i]] = i;
                    if(!inq[v[i]])
                    {
                        q[rear ++] = v[i], inq[v[i]] = 1;
                        rear > Q ? rear = 0 : 0;    
                    }    
                }
        }
        return dis[T] != INF;
    }
    void solve()
    {
        int i, c = 0, a, f = 0;
        if(!check())
        {
            printf("-1\n");
            return ;
        }
        floyd(), build();
        while(bfs())
        {
            for(i = pre[T], a = INF; i != -1; i = pre[v[i ^ 1]]) a = std::min(a, flow[i]);
            for(i = pre[T]; i != -1; i = pre[v[i ^ 1]])
                flow[i] -= a, flow[i ^ 1] += a;
            f += a, c += a * dis[T];
        }
        printf("%d\n", f == TOT ? c + SUM : -1);
    }
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t --)
        {
            init();
            solve();    
        }
        return 0;    
    }
  • 相关阅读:
    Windows Live Writer介绍及相关问题解决
    阳光盒子火狐浏览器
    Cracking Wifi Wpa-Wpa2 in 5 second——Dumpper V.80.8 +JumpStart+WinPcap
    叮咚recovery——想刷什么包就刷什么包
    vivo s11t线刷救砖过程
    syskey——让你的电脑更加安全
    滑动关机
    QQ空间点赞大法+浏览器自动翻页脚本=点赞根本停不下来
    Kon-boot v2.5介绍与使用方法总结(支持win10)
    Mimikatz的使用心得
  • 原文地址:https://www.cnblogs.com/staginner/p/2640420.html
Copyright © 2011-2022 走看看