1. 程式人生 > >hdu4685Prince and Princess 二分圖+Tarjan縮點

hdu4685Prince and Princess 二分圖+Tarjan縮點

hdu4685Prince and Princess

題目傳送門

分析

題目大意:一個王子喜歡好多妹子,現在要讓最多的王子妹子雙宿雙飛,同時求出一個王子可以和多少個妹子結婚並且不影響別的王子妹子雙宿雙飛的對數。
很強的一道二分圖,是poj1904的加強版本。
有一個正經的名字叫二分圖匹配的可行邊。
首先考慮完備匹配的情況。
一種很神仙的做法是,王子照樣向妹子連邊,但是如果某個妹子匹配了王子,把那個妹子向王子連邊,然後跑Tarjan,在一個強連通分量裡的王子可以任意匹配妹子。
因為如果幾個王子和妹子在同一個強連通分量裡,由於一個妹子只會連回去一條邊,所以說如果一個王子想要

一個妹子他必須自己動手。否則的話他一定沒辦法通過別的王子到達那個妹子,這樣每個王子必定可以任意喜歡自己連通塊裡的妹子啦!(亂倫既視感)
然後如果去搞別連通塊的妹子肯定會橫刀奪愛。這不清真啊
再考慮單身的情況。
沒有物件就創造物件,沒有機會就創造機會
讓結了婚的造幾個人出來啊
咳咳,假設匹配數為 r r ,那麼單身的 m
r m-r
個公主造 m r m-r 個王子匹配,單身的 n
r n-r
個王子造 n r n-r 個公主匹配。
新造的王子要喜歡所有人,新造的公主要被所有王子喜歡
然後匹配一波就是完備匹配啦。
硬是吧一道正經的二分圖變成了家庭倫理大劇。
過審堪憂啊。。。

程式碼

碼農題,Tarjan縮點忘光光了。
再次鄙視多組資料

#include<bits/stdc++.h>
#define mem(x) memset(x, 0, sizeof(x))
const int N = 2e3 + 10, M = N * N;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int pr[N], las[N], dfn[N], low[N], be[N], st[N], nx[M], vis[N], to[M], tm, tot, tp, cnt; bool mark[N], g[N][N];
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
bool dfs(int u, int m) {
    for(int i = 1;i <= m; ++i)
    if(vis[i] != tm && g[u][i]) {
        vis[i] = tm;
        if(!las[i] || dfs(las[i], m))
            return las[i] = u, true;
    }
    return false;
}
int Hungary(int n, int m) {
    int r = 0;
    for(int i = 1;i <= m; ++i) las[i] = 0;
    for(int i = 1;i <= n; ++i) ++tm, r += dfs(i, m); 
    return r;
}
void Tarjan(int u) {
    dfn[u] = low[u] = ++tot; st[++tp] = u; mark[u] = true;
    for(int i = pr[u]; i; i = nx[i]) 
        if(!dfn[to[i]]) Tarjan(to[i]), low[u] = std::min(low[u], low[to[i]]);
        else if(mark[to[i]]) low[u] = std::min(low[u], dfn[to[i]]);
    if(dfn[u] == low[u])  
        for(++cnt;st[tp + 1] != u; mark[st[tp--]] = false)
            be[st[tp]] = cnt;
}
void Clr() {
    cnt = tp = 0;
    mem(pr); mem(g); mem(st); mem(dfn); mem(low); mem(mark);
}
int main() {
    for(int C = 1, T = ri(); C <= T; ++C) {
        Clr(); int n = ri(), m = ri();
        for(int i = 1;i <= n; ++i)
            for(int x = ri();x--;) g[i][ri()] = true;
        int r = Hungary(n, m);
        int nn = n + m - r;
        for(int i = n + 1;i <= nn; ++i)
            for(int j = 1;j <= nn; ++j)
                g[i][j] = true;
        for(int i = 1;i <= n; ++i)
            for(int j = m + 1;j <= nn; ++j)
                g[i][j] = true;
        Hungary(nn, nn); 
        for(int i = 1;i <= nn; ++i)
            if(las[i]) add(i + nn, las[i]);
        for(int i = 1;i <= nn; ++i)
            for(int j = 1; j <= nn; ++j)
                if(g[i][j]) add(i, j + nn);
        for(int i = 1;i <= nn << 1; ++i) if(!dfn[i]) tp = tot = 0,Tarjan(i);
        printf("Case #%d:\n", C);
        for(int i = 1;i <= n; ++i) {
            tp = 0;
            for(int j = 1;j <= m; ++j) if(g[i][j] && be[j + nn] == be[i]) st[++tp] = j;
            printf("%d", tp);
            for(int j = 1;j <= tp; ++j) printf(" %d", st[j]); 
            puts("");
        }
    }
    return 0;
}