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\n", vt.size()); for (int i = 0; i < (int)vt.size(); ++i) { printf(i == 0 ? "%d" : " %d", vt[i]); } puts(""); } return 0; }