King's Quest---poj1904(连通图缩点)

题目链接:http://poj.org/problem?id=1904

题意:国王有n个儿子,每个儿子喜欢ki个女孩,国王想让王子与他喜欢的人结婚,就让巫师做一个列表出来,但是国王想知道王子能和哪些女孩结婚,并且不影响其他王子也能与自己喜欢的女孩在一起;

其实就是求王子能和哪些女孩结婚,不影响最大匹配;

 假如已知匹配王子属于x,女孩属于y

现有匹配xi---yi xj---yj 并且xi也喜欢yj

如果说xi能和yj匹配并不改变匹配总数,必须满足一下两个条件之一:

1:xj也喜欢yi;

2:yi 被某个匹配中的 xk喜欢,并且 xi 也喜欢 yk ;

思路很像匈牙利算法中找曾广路的过程;xi--->yj--->xk--->yi--->xi(是一个环,各点都能相互到达,所以其中的匹配可以互换,并且不影响匹配总数);

 

建图:王子u喜欢女孩v,则u到v连一条边。对于给出的初始完美匹配,王子u与女孩v匹配,则v到u连一条边。然后求SCC。

显然对于同一个SCC中王子数目和女孩数目是相等的,并且从某个王子出发能够到达所有女孩,这样,王子可以和属于同一个SCC中的任意一个女孩结婚,而不会影响其他王子。

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
#define met(a, b) memset(a, b, sizeof(a))
const int N = 4050;

int n, low[N], dfn[N], Time;
int IsSta[N], Sta[N], top, Belong[N], cnt, ans[N];
vector<vector<int> >G;

void Init()
{
    met(low, 0);
    met(dfn, 0);
    met(IsSta, 0);
    met(Sta, 0);
    met(Belong, 0);
    G.clear();
    G.resize(N+1);
    Time = cnt = top = 0;
}

void Tarjan(int u)
{
    low[u] = dfn[u] = ++ Time;
    IsSta[u] = 1;
    Sta[top++] = u;
    int len = G[u].size(), v;
    for(int i=0; i<len; i++)
    {
        v = G[u][i];
        if(!dfn[v])
        {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(IsSta[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        ++ cnt;
        do
        {
            v = Sta[--top];
            IsSta[v] = 0;
            Belong[v] = cnt;
        }while(u != v);
    }
}

int main()
{
    while(scanf("%d", &n) != EOF)
    {
        Init();

        int k, x;

        for(int i=1; i<=n; i++)
        {
            scanf("%d", &k);
            for(int j=1; j<=k; j++)
            {
                scanf("%d", &x);
                G[i].push_back(x+n);///王子编号为1---n,公主编号为n+1---2*n;
            }
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d", &x);
            G[x+n].push_back(i);///添加反向边;
        }

        for(int i=1; i<=n; i++)
        {
            if(!dfn[i])
                Tarjan(i);
        }

        for(int i=1; i<=n; i++)
        {
            int len = G[i].size(), u = Belong[i], v;

            met(ans, 0);
            int K = 0;

            for(int j=0; j<len; j++)
            {
                v = Belong[ G[i][j] ];
                if( u == v )///当他们在一个块中时是可以与之匹配的;
                    ans[K++] = G[i][j];
            }

            sort(ans, ans+K);

            printf("%d", K);
            for(int j=0; j<K; j++)
                printf(" %d", ans[j]-n);///恢复原编号;
            printf("
");
        }
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zhengguiping--9876/p/5676699.html