HDU3488 Tour 再次感受到KM的强大

题意:这题自己YY了下,没想到结论还是对的。题目告诉我们一个有向图,现在问将图中的每一个点都划分到一个环中的最少代价是多少?每条边都有一个代价。

解法:由于要成环,那么将这个图进行拆点,就变成了单向的二分图了,此时一个完备匹配就是一种连线策略,只要保证没有边是和自己相连,就能够满足题目中要求的每个点至少属于一个环。证明也是很简单的。因为我们总可以从一个完备匹配中找出起点,然后再从匹配点作为起点找......

左图可以看做是1,2成环,3,4,5成环。

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

int N, M;

const int INF = 0x3f3f3f3f;
int w[205][205];
int lx[205], ly[205];
int sx[205], sy[205];
int match[205], slack[205];

int path(int u) {
    sx[u] = 1;
    for (int i = 1; i <= N; ++i) {
        if (sy[i]) continue;
        int t = lx[u] + ly[i] - w[u][i];
        if (!t) {
            sy[i] = 1;
            if (!match[i] || path(match[i])) {
                match[i] = u;
                return true;
            }
        } else {
            slack[i] = min(slack[i], t);
        }
    }
    return false;
}

void KM() {
    memset(match, 0, sizeof (match));
    memset(lx, 0x80, sizeof (lx));
    memset(ly, 0, sizeof (ly));
    for (int i = 1; i <= N; ++i) {
        for (int j = 1; j <= N; ++j) { 
            lx[i] = max(lx[i], w[i][j]);
        }
    }
    for (int i = 1; i <= N; ++i) {
        memset(slack, 0x3f, sizeof (slack));
        while (1) {
            memset(sx, 0, sizeof (sx));
            memset(sy, 0, sizeof (sy));
            if (path(i)) break;
            int d = INF;
            for (int j = 1; j <= N; ++j) {
                if (!sy[j]) d = min(d, slack[j]);
            }
            for (int j = 1; j <= N; ++j) {
                if (sx[j]) lx[j] -= d;
                if (sy[j]) ly[j] += d;
                else slack[j] -= d;
            }
        }
    }
    int ret = 0;
    for (int i = 1; i <= N; ++i) {
        ret += w[match[i]][i];
    }
    printf("%d\n", -ret);
}

int main() {
    int T, x, y, ct;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &N, &M);
        memset(w, 0x80, sizeof (w));
        for (int i = 1; i <= M; ++i) {
            scanf("%d %d %d", &x, &y, &ct);
            w[x][y] = max(w[x][y], -ct);
        }
        KM();
    }
    return 0;    
}
原文地址:https://www.cnblogs.com/Lyush/p/3025145.html