码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 3488--Tour(KM or 费用流)

时间:2016-09-13 01:25:34      阅读:160      评论:0      收藏:0      [点我收藏+]

标签:

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

这题有坑,有重边。。

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\n", -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\n", 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\n", MCMF());
    }
    return 0;
}
View Code

 

HDU 3488--Tour(KM or 费用流)

标签:

原文地址:http://www.cnblogs.com/wenruo/p/5866913.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!