POJ 1904 King's Quest(二分图由已知完美匹配求全部完美匹配)

87 篇文章 0 订阅

King's Quest
Time Limit: 15000MS Memory Limit: 65536K
Total Submissions: 9113 Accepted: 3340
Case Time Limit: 2000MS

Description

Once upon a time there lived a king and he had N sons. And there were N beautiful girls in the kingdom and the king knew about each of his sons which of those girls he did like. The sons of the king were young and light-headed, so it was possible for one son to like several girls. 

So the king asked his wizard to find for each of his sons the girl he liked, so that he could marry her. And the king's wizard did it -- for each son the girl that he could marry was chosen, so that he liked this girl and, of course, each beautiful girl had to marry only one of the king's sons. 

However, the king looked at the list and said: "I like the list you have made, but I am not completely satisfied. For each son I would like to know all the girls that he can marry. Of course, after he marries any of those girls, for each other son you must still be able to choose the girl he likes to marry." 

The problem the king wanted the wizard to solve had become too hard for him. You must save wizard's head by solving this problem. 

Input

The first line of the input contains N -- the number of king's sons (1 <= N <= 2000). Next N lines for each of king's sons contain the list of the girls he likes: first Ki -- the number of those girls, and then Ki different integer numbers, ranging from 1 to N denoting the girls. The sum of all Ki does not exceed 200000. 

The last line of the case contains the original list the wizard had made -- N different integer numbers: for each son the number of the girl he would marry in compliance with this list. It is guaranteed that the list is correct, that is, each son likes the girl he must marry according to this list. 

Output

Output N lines.For each king's son first print Li -- the number of different girls he likes and can marry so that after his marriage it is possible to marry each of the other king's sons. After that print Li different integer numbers denoting those girls, in ascending order.

Sample Input

4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4

Sample Output

2 1 2
2 1 2
1 3
1 4

Hint

This problem has huge input and output data,use scanf() and printf() instead of cin and cout to read data to avoid time limit exceed. 

Source


题目大意:

    国王有N个儿子,每个儿子都有几个喜欢的女孩(一共有N的女孩),现在已经知道一种可以让每一个王子都和其中一个自己喜欢的女孩结婚的方案,求每个王子能够结婚且不会导致其他王子无法结婚的女孩。


解题思路:

    题目给的明显是一个二分图,首先很自然的就想到二分图匹配。这样我们就可以先想到一个朴素的算法:删除一个王子和一个他喜欢的女孩,然后对剩下的人进行二分图匹配,若匹配数是N-1则这一对满足要求,否则不满足。不过这样的复杂度是O(E*N^3),显然时间复杂度太高。

    不过,我们还没有用上题目已给的一个完美匹配,而且我们也不需要求出具体的匹配,只需要判断一条边是否能在完美匹配中即可。还是从二分图匹配开始思考,假设开始ai与bi匹配,aj匹配bj。如果我们想让与ai匹配bj,aj与匹配bi,那么就会产生这样一条增广路:bi->ai->bj->aj->bi,这时我们可以发现增广路形成了一个有向环,于是我们就可以按照刚才的增广方式建图:若ai喜欢bj则连接一条从ai到的bj边,若起始ai于bj匹配,则再连接一条从bjai的边。在这个图上,如果在一个强连通分量上并且喜欢,就说明他们可以通过增广匹配到一起。所以我们只需要求强连通分量即可。


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
using namespace std;

const int MAXN=2000+3;
const int MAXV=MAXN*2;
int N,vis[MAXV],dfn[MAXV],low[MAXV],belong[MAXV],n_dfn,n_gruop;
vector<int> G[MAXV],save[MAXN];
stack<int> st;
bool G0[MAXN][MAXN];//原始的喜欢关系

void tarjan(int u)//tarjan算法求强连通分量
{
    vis[u]=1;
    dfn[u]=low[u]=++n_dfn;
    st.push(u);
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(!vis[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]==1)
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ++n_gruop;
        while(true)
        {
            int v=st.top(); st.pop();
            belong[v]=n_gruop;
            if(v>N)
                save[n_gruop].push_back(v-N);
            vis[v]=2;
            if(v==u)
                break;
        }
    }
}

void init()//初始化
{
    for(int i=1;i<=2*N;++i)
    {
        G[i].clear();
        vis[i]=0;
    }
    for(int i=1;i<=N;++i)
    {
        save[i].clear();
        for(int j=1;j<=N;++j)
            G0[i][j]=false;
    }
    n_dfn=n_gruop=0;
}

int main()
{
    while(~scanf("%d",&N))
    {
        init();
        for(int u=1;u<=N;++u)
        {
            int num;
            scanf("%d",&num);
            for(int i=0;i<num;++i)
            {
                int v;
                scanf("%d",&v);
                G[u].push_back(v+N);
                G0[u][v]=true;
            }
        }
        for(int u=1;u<=N;++u)
        {
            int v;
            scanf("%d",&v);
            G[v+N].push_back(u);
        }
        for(int i=1;i<=N*2;++i)
            if(!vis[i])
                tarjan(i);
        for(int i=1;i<=n_gruop;++i)
            sort(save[i].begin(),save[i].end());
        for(int i=1;i<=N;++i)
        {
            int g=belong[i],cnt=0;
            for(int j=0;j<save[g].size();++j)
                if(G0[i][save[g][j]])
                    ++cnt;
            printf("%d",cnt);
            for(int j=0;j<save[g].size();++j)
                if(G0[i][save[g][j]])
                    printf(" %d",save[g][j]);
            putchar('\n');
        }
    }
    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值