POJ 1904 King's Quest ★(强连通分量:可行完美匹配边)

题意

有n个女生和n个男生,给定一些关系表示男生喜欢女生(即两个人可以结婚),再给定一个初始匹配,表示这个男生和哪个女生结婚,初始匹配必定是合法的.求每个男生可以和哪几个女生可以结婚且能与所有人不发生冲突。

思路

好题啊。。。没想到强连通分量还能应用到完美匹配上。。。 将男生从1到n编号,女生从(n+1)到2*n编号,输入时如果男生u可以和女生v结婚,那么就做一条从u到v的边(u,v),对于输入的初始匹配(u,v)(表示男生u和女生v结婚),那么从v做一条到u的边(v,u),然后求解改图的强连通分量,如果男生和他喜欢的女生在同一个强连通分量内,则他们可以结婚(匹配) 为什么呢?因为如果这个男生找另一个女生结婚,则因为在同一个强连通分量中,则这个女生到男生原配存在路径,那么这样原配一定存在别的某个男生可以和她结婚。 抽象一下,求所有可行的完美匹配边①按原图建立无向边形成二分图。 ②求出任意一个完美匹配。 ③建立新图,原图改为有向边<i, j>,并且对于每一个完美匹配(i, j),连一条<j, i>的边。 ④求强连通分量,如果原图某条边<i, j>两点在同一个强连通分量,则该边可以是完美匹配边。  

代码

 
using namespace std;
const int MAXN = 4005;
const int MAXM = 205005;
struct SCC{
    int scc_num, scc[MAXN];         //强连通分量总数、每个节点所属的强连通分量
    vector  scc_node[MAXN];    //强连通分量中的节点
    stack  st;
    int dfn[MAXN], low[MAXN], id;
    bool vis[MAXN], instack[MAXN];
    int cnt, head[MAXN];

    struct node{
        int u, v;
        int next;
    }arc[MAXM];

    void init(){
        cnt = 0;
        mem(head, -1);
        return ;
    }
    void add(int u, int v){
        arc[cnt].u = u;
        arc[cnt].v = v;
        arc[cnt].next = head[u];
        head[u] = cnt ++;
    }
    void dfs(int u){
        vis[u] = instack[u] = 1;
        st.push(u);
        dfn[u] = low[u] = ++ id;
        for (int i = head[u]; i != -1; i = arc[i].next){
            int v = arc[i].v;
            if (!vis[v]){
                dfs(v);
                low[u] = min(low[u], low[v]);
            }
            else if (instack[v]){
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (low[u] == dfn[u]){
            ++ scc_num;
            while(st.top() != u){
                scc[st.top()] = scc_num;
                scc_node[scc_num].push_back(st.top());
                instack[st.top()] = 0;
                st.pop();
            }
            scc[st.top()] = scc_num;
            scc_node[scc_num].push_back(st.top());
            instack[st.top()] = 0;
            st.pop();
        }
        return ;
    }
    void tarjan(int n){
        mem(vis, 0);
        mem(instack, 0);
        mem(dfn, 0);
        mem(low, 0);
        mem(scc, 0);
        id = scc_num = 0;
        for (int i = 0; i <= n; i ++)
            scc_node[i].clear();
        while(!st.empty())
            st.pop();
        for (int i = 1; i <= n; i ++){   //枚举节点
            if (!vis[i])
                dfs(i);
        }
        return ;
    }
}S;
bool like[MAXN][MAXN];
vector  girl;
void solve(int n){
    S.tarjan(n+n);
    for (int i = 1; i <= n; i ++){
        int i_scc = S.scc[i];
        girl.clear();
        for (int j = 0; j < (int)S.scc_node[i_scc].size(); j ++){
            int node = S.scc_node[i_scc][j];
            if (node > n && like[i][node - n]){
                girl.push_back(node - n);
            }
        }
        sort(girl.begin(), girl.end());
        printf("%d", girl.size());
        for (int j = 0; j < (int)girl.size(); j ++){
            printf(" %d", girl[j]);
        }
        puts("");
    }
}
int main(){
	//freopen("test.in", "r", stdin);
	//freopen("test.out", "w", stdout);
    int n;
    while(scanf("%d", &n) != EOF){
        S.init();
        mem(like, 0);
        for (int i = 1; i <= n; i ++){
            int num;
            scanf("%d", &num);
            for (int j = 0; j < num; j ++){
                int girl;
                scanf("%d", &girl);
                S.add(i, girl + n);
                like[i][girl] = true;
            }
        }
        for (int i = 1; i <= n; i ++){
            int girl;
            scanf("%d", &girl);
            S.add(girl+n, i);
        }
        solve(n);
    }
	return 0;
}
 
原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114288.html