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
原文地址:https://www.cnblogs.com/wenruo/p/5866913.html