UVALive-2966 King's Quest(强连通+二分图匹配)

题目大意:有n个男孩和和n个女孩,已只每个男孩喜欢的女孩。一个男孩只能娶一个女孩、一个女孩只能嫁一个男孩并且男孩只娶自己喜欢的女孩,现在已知一种他们的结婚方案,现在要求找出每个男孩可以娶的女孩(娶完之后不能影响其他男孩结婚)。

题目分析:已知的结婚方案是一个完全匹配。从每个男孩出发向他喜欢的女孩连一条有向边,得到一张完全二分图,实际上这道题是让判断去掉哪一些边使图仍然完全匹配。设男生x1和女生y1是已知方案中要结婚的两个人,假如x1抛弃y1,选择了他也喜欢的y2结婚(也就是去掉边x1->y2),那么就得需要让原方案中y2的结婚对象x2选择一个他喜欢的女孩(不能再是y2)结婚,一直进行下去这个过程,y1终究会被选走(如果去边之后的图仍完全匹配)。这正是匈牙利算法的过程,但这样要超时。但是,如果将从y1出发连一条边到x1,那么这个过程所经过的所有点就构成了一个强连通分量。对于某个男孩,娶和他在同一强连通分量的任何一个女孩都不会影响其他男孩结婚。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<stack>
# include<vector>
# include<cstring>
# include<iostream>
using namespace std;
# define REP(i,s,n) for(int i=s;i<n;++i)
# define CL(a,b) memset(a,b,sizeof(a))
# define CLL(a,b,n) fill(a,a+n,b)

const int N=2005;
struct Edge
{
    int to,nxt;
};
Edge e[N*100+N];
stack<int>S;
int scc_cnt,dfs_cnt,cnt,n,head[2*N],sccno[2*N];
int low[2*N],pre[2*N],G[N][N];
vector<int>ans;

void add(int u,int v)
{
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}

void dfs(int u)
{
    S.push(u);
    low[u]=pre[u]=++dfs_cnt;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(!pre[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
        }else if(!sccno[v])
            low[u]=min(low[u],pre[v]);
    }
    if(low[u]==pre[u]){
        ++scc_cnt;
        while(1){
            int x=S.top(); S.pop();
            sccno[x]=scc_cnt;
            if(x==u) break;
        }
    }
}

void findScc()
{
    scc_cnt=dfs_cnt=0;
    CL(pre,0);
    CL(sccno,0);
    REP(i,1,n+1) if(!pre[i]) dfs(i);
}

int main()
{
    while(~scanf("%d",&n))
    {
        int k,a;
        cnt=0;
        CL(G,0);
        CL(head,-1);
        REP(i,1,n+1){
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d",&a);
                add(i,a+n);
                G[i][a]=1;
            }
        }
        REP(i,1,n+1){
            scanf("%d",&a);
            add(a+n,i);
        }
        findScc();
        REP(i,1,n+1){
            ans.clear();
            REP(j,n+1,2*n+1) if(G[i][j-n]&&sccno[i]==sccno[j])
                ans.push_back(j-n);
            printf("%d",ans.size());
            REP(j,0,ans.size()) printf(" %d",ans[j]);
            printf("\n");
        }
    }
    return 0;
}

  

posted @ 2015-11-08 13:05  20143605  阅读(288)  评论(0编辑  收藏  举报