2013 Multi-University Training Contest 9

HDU-4687 Boke and Tsukkomi

题意:给定一个简单图,询问哪些边如果选择的话会使得最大的连边数减少。

解法:套用一般图的最大匹配算法(带花树)先算出最大匹配数,然后枚举一条边被选择(注意:如果改变被选择,则两端点相邻的边都应删除),看是否只减少一条匹配边。

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

const int MAXN = 45;
int n, m;
int u[150], v[150];

struct Graph {

    bool mat[MAXN + 1][MAXN + 1];
    int n;

    bool inque[MAXN + 1];
    int que[MAXN], head, tail;

    int match[MAXN + 1], father[MAXN + 1], base[MAXN + 1];
    
    int inpath[MAXN + 1];
    static int pcnt;               // count of paths have existed

    int inblossom[MAXN + 1];
    static int bcnt;               // count of blossoms have existed

    void init(int _n) {
        n = _n;
        for (int i = 1; i <= n; ++i) {
            match[i] = 0;
            for (int j = 1; j <= n; ++j)
                mat[i][j] = false;
        }
    }

    int pop() { return que[head++]; }

    void push(int x) {
        que[tail++] = x;
        inque[x] = true;
    }

    void add_edge(int a, int b) {
        mat[a][b] = mat[b][a] = true;
    }


    int find_ancestor(int u, int v) {
        ++pcnt;
        while (u) {
            u = base[u];
            inpath[u] = pcnt;
            u = father[match[u]];
           // if match[u] == 0, meaning u is the root node, it also works, because father[0] == 0
        }
        while (true) {
            v = base[v];
            if (inpath[v] == pcnt) return v;
            v = father[match[v]];
        }
    }

    void reset(int u, int anc) {
        while (u != anc) {
            int v = match[u];
            inblossom[base[v]] = bcnt;
            inblossom[base[u]] = bcnt;
            v = father[v];
            if (base[v] != anc) father[v] = match[u];
            u = v;
        }
    }

    void contract(int u, int v) {
        int anc = find_ancestor(u, v);
        ++bcnt;
        reset(u, anc);
        reset(v, anc);
        if (base[u] != anc) father[u] = v;
        if (base[v] != anc) father[v] = u;
        for (int i = 1; i <= n; ++i)
            if (inblossom[base[i]] == bcnt) {
                base[i] = anc;
                if (!inque[i]) push(i);
            }
    }

    int find_augment(int start) {
        for (int i = 1; i <= n; ++i) {
            father[i] = 0;
            inque[i] = false;
            base[i] = i;
        }
        head = 0; tail = 0; push(start);
        while (head < tail) {
            int u = pop();
            for (int v = 1; v <= n; ++v)
                if (mat[u][v] && base[v] != base[u] && match[v] != u) {
                    if (v == start || (match[v] && father[match[v]]))
                                                   // node v is out-point
                        contract(u, v);               // make blossom
                    else {
                        if (father[v] == 0) {       // not in-point
                            if (match[v]) {           // has matched
                                push(match[v]);       // match[v] becomes out-point
                                father[v] = u;       // v becomes in-point
                            } else {
                                father[v] = u;       // node v is another end
                                return v;
                            }
                        }
                    }
                }
        }
        return 0;
    }

    void augment(int finish) {
        int u = finish, v, w;
        while (u) {
            v = father[u];
            w = match[v];
            match[u] = v;
            match[v] = u;
            u = w;
        }
    }

    int graph_max_match() {
        int ans = 0;
        for (int i = 1; i <= n; ++i)
            if (match[i] == 0) {
                int finish = find_augment(i);
                if (finish) {
                    augment(finish);
                    ans += 2;
                }
            }
        return ans;
    }

} g;

int Graph::bcnt = 0, Graph::pcnt = 0;
vector<int>vt;

int main() {
    while (scanf("%d %d", &n, &m) != EOF) {
        g.init(n);
        vt.clear();
        bool first = true;
        for (int i = 0; i < m; ++i) {
            scanf("%d %d", &u[i], &v[i]);
            g.add_edge(u[i], v[i]);
        }
        int MaxPair = g.graph_max_match() / 2; // 返回的是匹配的点数 
        for (int i = 0; i < m; ++i) {
            g.init(n);
            int a = u[i], b = v[i];
            for (int j = 0; j < m; ++j) { // 如果匹配这条边那么两个端点其他连边都是不能匹配的 
                if (u[j] == a || u[j] == b || v[j] == a || v[j] == b) continue;
                g.add_edge(u[j], v[j]);
            }
            int tmp = g.graph_max_match() / 2;
            if (tmp != MaxPair - 1) vt.push_back(i+1);
        }
        printf("%d
", vt.size());
        for (int i = 0; i < (int)vt.size(); ++i) {
            printf(i == 0 ? "%d" : " %d", vt[i]);
        }
        puts("");
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Lyush/p/3271491.html