zoukankan      html  css  js  c++  java
  • HDU 3488--Tour(KM or 费用流)

    因为每个点只能经过一次 所以考虑拆点

    这题有坑,有重边。。

    KM算法

    把一个点拆成入点和出点 入点在X部,出点在Y步。

    如果u,v之间有路径,就在X部的u点连接Y部的v点

    求完美匹配。

    当完美匹配的时候,每个点都有一个入度和一个出度,可知成环。

    因为完美匹配求得是最大匹配

    记得把每条边权值取相反数

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    
    using namespace std;
    const int MAXN = 205;
    const int INF = 0x3f3f3f3f;
    
    int G[MAXN][MAXN];
    int vx[MAXN], vy[MAXN];
    bool visx[MAXN], visy[MAXN];
    int match[MAXN];
    int slack[MAXN];
    
    int N, M;
    
    bool dfs(int x)
    {
        visx[x] = true;
    
        for (int y = 0; y < N; ++y) {
    
            if (visy[y]) continue;
    
            int gap = vx[x] + vy[y] - G[x][y];
    
            if (gap == 0) {
                visy[y] = true;
                if (match[y] == -1 || dfs( match[y] )) {
                    match[y] = x;
                    return true;
                }
            } else {
                slack[y] = min(slack[y], gap);
            }
        }
    
        return false;
    }
    
    int KM()
    {
        memset(match, -1, sizeof match);
        memset(vy, 0, sizeof vy);
    
        for (int i = 0; i < N; ++i) {
            vx[i] = G[i][0];
            for (int j = 1; j < N; ++j) {
                vx[i] = max(vx[i], G[i][j]);
            }
        }
    
        for (int i = 0; i < N; ++i) {
    
            fill(slack, slack + N, INF);
    
            while (1) {
                memset(visx, false, sizeof visx);
                memset(visy, false, sizeof visy);
    
                if (dfs(i)) break;
    
                int d = INF;
                for (int j = 0; j < N; ++j)
                    if (!visy[j]) d = min(d, slack[j]);
    
                for (int j = 0; j < N; ++j) {
                    if (visx[j]) vx[j] -= d;
                    if (visy[j]) vy[j] += d;
                    else slack[j] -= d;
                }
            }
        }
    
        int res = 0;
        for (int i = 0; i < N; ++i)
            res += G[ match[i] ][i];
    
        return res;
    }
    
    int Scan() {
        int res = 0, flag = 0;
        char ch;
        if((ch = getchar()) == '-') flag = 1;
        else if(ch >= '0' && ch <= '9') res = ch - '0';
        while((ch = getchar()) >= '0' && ch <= '9')
            res = res * 10 + (ch - '0');
        return flag ? -res : res;
    }
    
    int main()
    {
        int T = Scan();
        while (T--) {
            N = Scan(), M = Scan();
            for (int i = 0; i <= N; ++i) {
                for (int j = 0; j <= N; ++j) {
                    G[i][j] = -INF;
                }
            }
            int u, v, w;
            while (M--) {
                u = Scan()-1, v = Scan()-1, w = Scan();
                G[u][v] = max(G[u][v], -w);
            }
    
            printf("%d
    ", -KM());
        }
        return 0;
    }
    View Code

    费用流

    就是把上面的完美匹配用网络流来求。。

    和完美匹配一样

    如果u,v之间有路径,就u的入点连v的出点,然后所有入点连起点,所有出点连汇点,求最大流最小花费即可。

    费用流比起KM慢了几倍。。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <string>
    #include <vector>
    #include <bitset>
    #include <cstdio>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <list>
    #include <map>
    #include <set>
    #define pk(x) printf("%d
    ", x)
    using namespace std;
    #define PI acos(-1.0)
    #define EPS 1E-6
    #define clr(x,c) memset(x,c,sizeof(x))
    //#pragma comment(linker, "/STACK:102400000,102400000")
    
    typedef long long ll;
    #define CLR(x, v, n) memset(x, v, sizeof(x[0])*n)
    const int N = 410;
    const int M = 1000000;
    const int INF = 0x3f3f3f3f;
    
    struct Edge {
        int to, next, cap, flow, cost;
        void init(int _to, int _cap, int _cost, int _next) {
            to = _to; cap = _cap; cost = _cost; next = _next; flow = 0;
        }
    } edge[M];
    
    int head[N], cntE;
    int pre[N], dis[N];
    bool vis[N];
    int src, sink, tot;
    
    void dn(int &x, int y) { if(x>y) x=y; }
    
    void init() {
        cntE = 0;
        memset(head, -1, sizeof head);
    }
    
    void addedge(int u, int v, int cap, int cost) {
        edge[cntE].init(v, cap, cost, head[u]); head[u] = cntE++;
        edge[cntE].init(u, 0, -cost, head[v]); head[v] = cntE++;
    }
    
    bool spfa() {
        queue<int> q;
        fill(dis, dis+tot, INF); CLR(vis, false, tot); CLR(pre, -1, tot);
        dis[src] = 0; vis[src] = true;
        q.push(src);
        while (q.size()) {
            int u = q.front(); q.pop(); vis[u] = false;
            for (int i = head[u]; ~i; i = edge[i].next) {
                int v = edge[i].to;
                if (edge[i].cap > edge[i].flow && dis[u]+edge[i].cost < dis[v]) {
                    dis[v] = dis[u]+edge[i].cost;
                    pre[v] = i;
                    if (!vis[v]) {
                        vis[v] = true;
                        q.push(v);
                    }
                }
            }
        }
        if (pre[sink] == -1) return false;
        return true;
    }
    
    int MCMF() {
        int flow = 0;
        int cost = 0;
        while (spfa()) {
            int f = INF;
            for (int i = pre[sink]; ~i; i = pre[edge[i^1].to]) {
                dn(f, edge[i].cap - edge[i].flow);
            }
            for (int i = pre[sink]; ~i; i = pre[edge[i^1].to]) {
                edge[i].flow += f;
                edge[i^1].flow -= f;
                cost += edge[i].cost * f;
            }
            flow += f;
        }
        //return flow;
        return cost;
    }
    
    int Scan() {
        int res = 0, flag = 0;
        char ch;
        if((ch = getchar()) == '-') flag = 1;
        else if(ch >= '0' && ch <= '9') res = ch - '0';
        while((ch = getchar()) >= '0' && ch <= '9')
            res = res * 10 + (ch - '0');
        return flag ? -res : res;
    }
    
    int n, m;
    int G[205][205];
    int main()
    {
        int T = Scan();
        while (T--) {
            clr(head, -1);
            cntE = 0;
            n = Scan(), m = Scan();
            int u, v, w;
            src = 0, sink = n*2+1, tot = n*2+2;;
            for (int i = 1; i <= n; ++i) {
                addedge(src, i, 1, 0);
                addedge(i+n, sink, 1, 0);
            }
            for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) G[i][j] = INF;
            while (m--) {
                u = Scan(), v = Scan(), w = Scan();
                G[u][v] = min(G[u][v], w);
            }
            for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) {
                if (G[i][j] != INF) {
                    addedge(i, j+n, 1, G[i][j]);
                }
            }
            printf("%d
    ", MCMF());
        }
        return 0;
    }
    View Code

    上面的费用流太慢辣,又找了个快点的。嘿嘿XD

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <string>
    #include <vector>
    #include <bitset>
    #include <cstdio>
    #include <queue>
    #include <stack>
    #include <cmath>
    #include <list>
    #include <map>
    #include <set>
    #define pk(x) printf("%d
    ", x)
    using namespace std;
    #define PI acos(-1.0)
    #define EPS 1E-6
    #define clr(x,c) memset(x,c,sizeof(x))
    //#pragma comment(linker, "/STACK:102400000,102400000")
    
    typedef long long ll;
    
    const int MAXV = 410;
    const int INF = 1<<30;
    
    struct Edge { int to, cap, cost, rev; };
    vector<Edge> G[MAXV];
    int dist[MAXV], prv[MAXV], pre[MAXV], in[MAXV];
    queue<int> que;
    
    void addedge(int from, int to, int cap, int cost) {
        G[from].push_back((Edge){to, cap, cost, G[to].size()});
        G[to].push_back((Edge){from, 0, -cost, G[from].size()-1});
    }
    
    int min_cost_max_flow(int s, int t) { //, int f) {
        int res = 0;
        int f = 0;
        while (1) { //f > 0) {
            for (int i = 0; i <= t; ++i) dist[i] = INF, in[i] = 0;
            dist[s] = 0;
            while (!que.empty()) que.pop();
            in[s] = 1;
            que.push(s);
    
            while (!que.empty()) {
                int u = que.front(); que.pop(); in[u] = 0;
                for (int i = 0; i < G[u].size(); ++i) {
                    Edge &e = G[u][i];
                    if (e.cap > 0 && dist[e.to] > dist[u] + e.cost) {
                        dist[e.to] = dist[u] + e.cost;
                        prv[e.to] = u;
                        pre[e.to] = i;
                        if (in[e.to] == 0) {
                            in[e.to] = 1;
                            que.push(e.to);
                        }
                    }
                }
            }
    
            if (dist[t] == INF) break; //return -1;
    
            int d = INF; // d = f;
            for (int v = t; v != s; v = prv[v]) {
                d = min(d, G[prv[v]][pre[v]].cap);
            }
            f += d;
            res += d * dist[t];
            for (int v = t; v != s; v = prv[v]) {
                Edge &e = G[prv[v]][pre[v]];
                e.cap -= d;
                G[v][e.rev].cap += d;
            }
        }
        return res;
    }
    
    int Scan() {
        int res = 0, flag = 0;
        char ch;
        if((ch = getchar()) == '-') flag = 1;
        else if(ch >= '0' && ch <= '9') res = ch - '0';
        while((ch = getchar()) >= '0' && ch <= '9')
            res = res * 10 + (ch - '0');
        return flag ? -res : res;
    }
    
    int n, m;
    int mp[205][205];
    int main()
    {
        int T = Scan();
        while (T--) {
            n = Scan(), m = Scan();
            int u, v, w;
            int src = 0, sink = n*2+1;
            for (int i = 0; i <= sink; ++i) G[i].clear();
            for (int i = 1; i <= n; ++i) {
                addedge(src, i, 1, 0);
                addedge(i+n, sink, 1, 0);
            }
            for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) mp[i][j] = INF;
            while (m--) {
                u = Scan(), v = Scan(), w = Scan();
                mp[u][v] = min(mp[u][v], w);
            }
            for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) {
                if (mp[i][j] != INF) {
                    addedge(i, j+n, 1, mp[i][j]);
                }
            }
            printf("%d
    ", min_cost_max_flow(src, sink));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    A. Generous Kefa
    1031 骨牌覆盖
    1074 约瑟夫环 V2
    1073 约瑟夫环
    1562 玻璃切割
    Ants
    1024 矩阵中不重复的元素
    1014 X^2 Mod P
    1135 原根
    1010 只包含因子2 3 5的数
  • 原文地址:https://www.cnblogs.com/wenruo/p/5866913.html
Copyright © 2011-2022 走看看