HDU 4685 Prince and Princess

1010 Prince and Princess
对于这个题,首先我们考虑一个简化版,即如同样例给出来的有 n 个王
子和公主,并且他们能够完全匹配的情况。
我们先求出最大匹配,这时我们如果对于每个王子和他喜欢的每个公
主都去枚举他们 link 的时候是否还能找到最大匹配,这样复杂度可能高达
O ( n 4),应该是会超时的。
那么我们换一个角度,枚举然后找最大匹配实质上是找到一个人和他互
换,并且他们喜欢他们互相配对的公主。那么我们可以试着直接用连边来
表示这个过程,对于每个王子,从他目前配对的公主出发连有向边到他喜
欢的所有公主,表示他配对的公主单方面可以和这些公主换。若是两者可
以互换,那么在图中就表示为了可以互相到达,从而想到对于这样的一个
图求出他的强连通分量,对于每个王子,枚举他喜欢的每个公主,如果这
个公主和他目前配对的公主在同一个强连通分量里,表示这个王子可以选
该公主。
然后回到这个题,这个题变成了 n 个王子和 m 个公主,其实最主要的
还是他们的最大匹配不一定是完全匹配。如果直接套用以上的方法就有问
题。比如有一个王子,两个公主,这个王子两个公主都喜欢,那么求出的
最大匹配只会和其中一个公主相连,通过上面的方法建图,两个公主并不
会在一个强连通分量里面,但实际上这个王子选两个中的任意一个都不会
使最大匹配减少。我们要怎么解决这个问题,这里又要从最本质的地方来
看,就是一个互换。
这里的王子如果求出匹配连的是一号公主,那么二号公主没有和任何王
子相连,这个王子就无法和任何人互换从而得到二号公主。考虑到这个问
题,很自然的就能想到给二号公主虚拟一个王子和她配对就好了,然后这
个虚拟的王子喜欢所有的公主。
因为这个公主本身是没有匹配的,如果通过虚拟一个喜欢所有公主的王
子向所有其他公主连边,使得这个公主得到了一个匹配,那么肯定会有另
外一边的匹配数减少了,因为最开始就求到的是最大匹配,如果这个公主
匹配增加并且没有任何一边的匹配减少,肯定违背了最大匹配的性质。
所以得证这样一种虚拟王子连边的方法不会减少匹配数并且正确。
剩下公主的问题考虑完了,现在考虑剩下王子的问题。同理,我们给每
一个没有匹配上的剩下的王子,虚拟一个公主,这个公主被所有王子喜欢,
那么和最开始的做法一样求强连通然后考虑是否在一个强连通分量里即可。

证明和剩下公主类似。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include <algorithm>
using namespace std;
#define ll long long
#define prt(k) cerr<<#k" = "<<k<<endl
#include <vector>
const int N = 1010;
int n, m;
vector<int> g[N];
int from[N];
bool use[N];
int to[N];
bool match(int x)
{
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(use[v]) continue;
        use[v] = true;
        if(from[v]==-1 || match(from[v]))
        {
            from[v] = x;       to[x] = v;
            return true;
        }
    }
    return false;
}
int hungary()
{
    int tot = 0;
    memset(from, -1, sizeof from);
    for(int i=1;i<=n;i++)
    {
        memset(use, 0, sizeof use);
        if(match(i)) ++tot;
    }
    return tot;
}
vector<int> G[N];
int fa[N], scc, dfn[N], low[N], cur;
#include<stack>
stack<int> S;
int ins[N];
void dfs(int u)
{
    dfn[u] = low[u] = ++cur;
    S.push(u); ins[u]=1;
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u], low[v]);
        }
        else
        {
            if(ins[v])
                low[u]=min(low[u], dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        ++scc; int v;
        do {
            v=S.top(); S.pop();
            ins[v] = 0;
            fa[v] = scc;
        }while(v != u);
    }
}
int main()
{
    int re; cin>>re; int ca = 1;
    while(re--)
    {
        for(int i=0;i<N;i++) g[i].clear();
        for(int i=1;i<N;i++) G[i].clear();
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            int k; cin>>k;
            g[i].clear();
            for(int j=0;j<k;j++)
            {
                int x; scanf("%d", &x);
                g[i].push_back(x);
            }
        }

        int cnt = hungary();
      ///  for(int i=1;i<=m;i++) printf("from[%d] = %d\n", i, from[i]);
        int nn = n + m - cnt;
        bool vis[N];
        memset(vis, 0 ,sizeof vis);
        for(int i=1;i<=m;i++)
            if(from[i] != -1)
                vis[from[i]] = true;
        int xu = 0;
        for(int i=1;i<=n;i++)
            if(!vis[i])
                from[++xu+m] = i;
        for (int i=1;i<=xu+m;i++)
        {
            int x=from[i];
            if(x==-1)
            {
                for (int j=1; j<=m+xu; j++)
                    G[i].push_back(j);
            }
            else {
                for(int j=0;j<g[x].size();j++)
                {
                    G[i].push_back(g[x][j]);
                }
                for(int j=m+1;j<=m+xu;j++)
                    G[i].push_back(j);
            }
        }
        m += xu;
     /**   for(int i=1;i<=m;i++)
        {
            printf("%d : ", i);
            for(int j=0;j<G[i].size();j++)
                printf("%d ", G[i][j]);
            putchar(10);
        }   */
        memset(dfn, 0, sizeof dfn);
        memset(low, 0, sizeof low);
        memset(ins, 0 ,sizeof ins);
        scc  = cur = 0;
        for(int i=1;i<=m;i++)
            if(dfn[i]==0) dfs(i);
        vector<int> ans[N];
        for(int i=1;i<=m;i++)
        {
            if(from[i]==-1) continue;
            int u = from[i];
            for(int j=0;j<g[u].size();j++)
            {
                int v = g[u][j];
                if(fa[v] == fa[i])
                {
                     ans[u].push_back(v);
                }
            }
        }
       /// for(int i=1;i<=m;i++) printf("fa[%d] = %d\n", i, fa[i]);
        printf("Case #%d:\n", ca++);
        for(int i=1;i<=n;i++)
        {
            sort(ans[i].begin(), ans[i].end());
            int sz = ans[i].size();
            cout<<sz;
            for(int j=0;j<sz;j++) printf(" %d", ans[i][j]);
            putchar(10);
        }
    }
}
/**
34

2 1
1 1
1 1

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

1 2
2 1 2


*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值