SYSU-4, UVA 12711, 一般图最大(小)权匹配

题目大意:给你一个100个点的图,划分成两个点集,要求A点集所有点的度为奇数,B点集的点为偶数,求一个最小边权边集满足这个约束。

解:我们要先抽象提炼一些性质,才能得到这题一般图最小匹配的做法

首先一点,偶数点完全不用考虑,要么他们不选取,要么他们作为路径上的点即可(出度=入度),所以直接做一遍flyod,就可以把图变成只剩A点集的图了。

第二点,就是对于一个合法答案,一条边不可能被选取两遍或以上,因为这样可以可以去掉两次这条边,依然可以得到一个合法图,而且答案更小,这是用来证明上面抽象图的做法的正确性。

那么我们抽象了图以后,可以得知我们做一个最小匹配即可,因为这样能符合奇数点的约束,同时可以证明这样才是边权最小的选择(更大的图可以删边最终剩下匹配形式)

一般图的最小权匹配做法很多,另外一位大神的blog介绍了一种消圈的做法,但是我被xzj安利了一个随机算法去弄,感觉效果不错,代码也短而且好理解。

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <complex>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <deque>
#include <ctime>

using namespace std;

const double EPS = 1e-8;

#define ABS(x) ((x)<0?(-(x)):(x))
#define SQR(x) ((x)*(x))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))

#define LSON(x) ((x)<<1)
#define RSON(x) (((x)<<1)+1)
#define LOWBIT(x) ((x)&(-(x)))
#define MAXN 111
#define LL long long
#define OO 214748364

int w[MAXN][MAXN], g[MAXN][MAXN];
int match[MAXN], path[MAXN], d[MAXN], p[MAXN], len;
bool v[MAXN];
int n, m, k;

void init() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) g[i][j] = OO;
    for (int i = 0; i < m; ++i) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        --x; --y;
        if (x == y) continue;
        if (z < g[x][y]) {
            g[x][y] = g[y][x] = z;
        }
    }
    for (int k = 0; k < n; ++k) 
        for (int i = 0; i < n; ++i) 
            for (int j = 0; j < n; ++j) 
                g[i][j] = min(g[i][j], g[i][k] + g[k][j]);

    for (int i = 0; i < k; ++i) {
        for (int j = 0; j < k; ++j) {
            w[i][j] = OO - g[i][j];
        }
    }
}

bool dfs(int i) {
    path[len++] = i;
    if (v[i]) return true;
    v[i] = true;
    for (int j = 0; j < k; ++j) {
        if (i != j && match[i] != j && !v[j]) {
            int kok = match[j];
            if (d[kok] < d[i] + w[i][j] - w[j][kok]) {
                d[kok] = d[i] + w[i][j] - w[j][kok];
                if (dfs(kok)) return true;
            }
        }
    }
    --len;
    v[i] = false;
    return false;
}

void solve() {
    if (k&1) {
        puts("Impossible");
        return ;
    }
    for (int i = 0; i < k; ++i) p[i] = i, match[i] = i ^ 1;
    int cnt = 0;
    for (;;) {
        len = 0;
        bool flag = false;
        memset(d, 0, sizeof(d));
        memset(v, 0, sizeof(v));
        for (int i = 0; i < k; ++i) {
            if (dfs(p[i])) {
                flag = true;
                int t = match[path[len - 1]], j = len - 2;
                while (path[j] != path[len - 1]) {
                    match[t] = path[j];
                    swap(t, match[path[j]]);
                    --j;
                }
                match[t] = path[j];
                match[path[j]] = t;
                break;
            }
        }
        if (!flag) {
            if (++cnt >= 3) break;
            random_shuffle(p, p+k);
        }
    }
    int ans = 0;
    for (int i = 0; i < k; ++i) {
        int t = w[i][match[i]];
    //    cout << t << endl;
        if (t == 0) {
            puts("Impossible");
            return ;
        }
        ans += OO - t;
    }
    printf("%d\n", ans / 2);
}

int main() {
    freopen("test.txt", "r", stdin);
    srand(time(0));
    int cas; scanf("%d", &cas);
    for (int tt = 1; tt <= cas; ++tt) {
        printf("Case %d: ", tt);
        init();
        solve();
    }
    return 0;
}
UVA12711

 

posted @ 2016-07-04 11:19  F.D.His.D  阅读(2312)  评论(13编辑  收藏  举报