UESTC 898 方老师和缘分 --二分图匹配+强连通分量

这题原来以为是某种匹配问题,后来好像说是强连通的问题。

做法:建图,每个方老师和它想要的缘分之间连一条有向边,然后,在给出的初始匹配中反向建边,即如果第i个方老师现在找到的是缘分u,则建边u->i。这样求出所有的强连通分量,每个强连通分量中方老师和缘分的数目一定是相等的,所以每个方老师一定可以找到与他在同一个强连通分量里的缘分,因为强连通分量中每个点都是可达的,某个方老师找到了其强连通分量中的非原配点,则该原配缘分一定可以在强连通分量中找到"新欢"。可以画个图看看。

由于要构造非二分图,缘分的编号从n+1开始,到2n。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#define Mod 1000000007
using namespace std;
#define N 200007

std::vector<int> G[4005];
int low[4005],dfn[4005];
int instk[4005],bel[4005];
int n,Time,cnt,res;
stack<int> stk;
int ans[2005];


void Tarjan(int u)
{
    low[u] = dfn[u] = ++Time;
    stk.push(u);
    instk[u] = 1;
    for(int i=0;i<G[u].size();i++)
    {
        int v = G[u][i];
        if(!dfn[v])
        {
            Tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(instk[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v = stk.top();
            stk.pop();
            instk[v] = 0;
            bel[v] = cnt;
        }while(u != v);
    }
}

void init()
{
    memset(G,0,sizeof(G));
    memset(instk,0,sizeof(instk));
    memset(bel,-1,sizeof(bel));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    Time = cnt = 0;
    while(!stk.empty())
        stk.pop();
}

int main()
{
    int i,j,u,v,k;
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(i=1;i<=n;i++)
        {
            scanf("%d",&k);
            while(k--)
            {
                scanf("%d",&v);
                G[i].push_back(v+n);
            }
        }
        for(i=1;i<=n;i++)
        {
            scanf("%d",&v);
            G[v+n].push_back(i);
        }
        for(i=1;i<=n;i++)
        {
            if(!dfn[i])
                Tarjan(i);
        }
        for(u=1;u<=n;u++)
        {
            k = 0;
            for(i=0;i<G[u].size();i++)
            {
                v = G[u][i];
                if(bel[u] == bel[v])
                    ans[k++] = v-n;
            }
            sort(ans,ans+k);
            printf("%d",k);
            for(i=0;i<k;i++)
                printf(" %d",ans[i]);
            printf("\n");
        }
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/whatbeg/p/3765621.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值