Tour(二分图最大权匹配)(网络流)

Tour
Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%I64d & %I64u

Description

In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 30000) one-way roads connecting them. You are lucky enough to have a chance to have a tour in the kingdom. The route should be designed as: The route should contain one or more loops. (A loop is a route like: A->B->……->P->A.) 
Every city should be just in one route. 
A loop should have at least two cities. In one route, each city should be visited just once. (The only exception is that the first and the last city should be the same and this city is visited twice.) 
The total distance the N roads you have chosen should be minimized. 
 

Input

An integer T in the first line indicates the number of the test cases. 
In each test case, the first line contains two integers N and M, indicating the number of the cities and the one-way roads. Then M lines followed, each line has three integers U, V and W (0 < W <= 10000), indicating that there is a road from U to V, with the distance of W. 
It is guaranteed that at least one valid arrangement of the tour is existed. 
A blank line is followed after each test case.
 

Output

For each test case, output a line with exactly one integer, which is the minimum total distance.
 

Sample Input

1 6 9 1 2 5 2 3 5 3 1 10 3 4 12 4 1 8 4 6 11 5 4 7 5 6 9 6 5 4
 

Sample Output

42




题意:给你一个有向图,边有权值,现在要你求若干个环包含所有的顶点,并且每个顶点只出现一次(除了一个环中的起始点)使得华中所有边得权值之和最小。(这道题没有说明没有环的情况,直接按照都有环的情况做就行了)

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

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


像这杨构成圈并且每个点只能属于一个圈的题, 可以转化成2 分图, 每个点只能属于一个圈, 那么出度和入度必定为1 , 那么把一个点拆开i, i`, i控制入读, i` 控制出度, 流量只能为1 。 那么对于原来途中有的边 可以 i - > j`, j - > i`;连起来构图, 然后建立超级远点s,超级汇点t,s - > i , i` - > t ; 然后求最小费用流。。这样就抱着了每个点只能属于一个圈, 因为入读 == 出度 == 1 ;这类也问题可以  做为判断性问题出。


因为出入度 都是1 所以也可以用 km 求最值。。

代码:

#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
", -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;    
}

用最小费用最大流:

http://www.tuicool.com/articles/aUBvyeN





原文地址:https://www.cnblogs.com/zswbky/p/6717972.html