HDU 4685 二分图匹配+tarjan

56 篇文章 0 订阅
21 篇文章 0 订阅

这题跟POJ的1904很像。

大意就是

有n个王子和m个公主

每个王子都会喜欢若干个公主,也就是王子只跟自己喜欢的公主结婚

公主就比较悲惨, 跟谁结婚都行

然后输出王子可能的结婚对象

必须保证王子与任意这些对象中的一个结婚,都不会影响到剩余的王子的配对数,也就是不能让剩余的王子中突然有一个人没婚可结了


然后思路其实跟POJ1904非常像。

但是POJ那题给出了初始的一个完备匹配。

而本题没有

怎么办呢

我们就要想方设法构造出完备匹配

刚开始的时候我们可以先做一遍二分图最大匹配

那么因为两边人数不一等相等,所以可能有人会单身

那么这时候我们就要给这些单身的人安排对象了。

安排的对象不能是已有的这些人中的任何一个,那就只好再虚拟一些人出来了。

这些人比较杯具啊,对象都是虚拟的。

假设王子有剩下的,那么每个剩下的王子就连一个虚拟的公主,这个公主被所有的王子都喜欢。

假设公主有剩下的,那么每个剩下的公主就连一个虚拟的王子,这个王子喜欢所有的公主


然后就构造成完备匹配了。在下面的步骤就跟1904一摸一样了

上代码了 ,加了输入优化就比较爽。   如果有错误。请读者指出。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#define MAXN 2005
#define INF 100000000
#define eps 1e-7
using namespace std;
struct Edge
{
    int v, next;
}edge[200 * MAXN], newedge[255555];
int head[MAXN], e, visited[MAXN], newhead[MAXN], newe;
int top, scc, index;
int low[MAXN], dfn[MAXN], instack[MAXN];
int order[MAXN], cnt, st[MAXN], fa[MAXN];
int mark[MAXN], cx[MAXN], cy[MAXN];
int n, m;
int nt, all;
void init()
{
    top = scc = index = e = 0;
    memset(head, -1, sizeof(head));
    memset(dfn, 0, sizeof(dfn));
    memset(instack, 0, sizeof(instack));
    memset(newhead, -1, sizeof(newhead));
    newe = 0;
}
inline void insert(const int &x, const int &y)
{
    edge[e].v = y;
    edge[e].next = head[x];
    head[x] = e++;
}
inline int in()
{
    char ch;
    int a = 0;
    while((ch = getchar()) == ' ' || ch == '\n');
    a += ch - '0';
    while((ch = getchar()) != ' ' && ch != '\n')
    {
        a *= 10;
        a += ch - '0';
    }
    return a;
}
inline void out(int a)
{
    if(a >= 10)out(a / 10);
    putchar(a % 10 + '0');
}
inline void newinsert(const int &x, const int &y)
{
    newedge[newe].v = y;
    newedge[newe].next = newhead[x];
    newhead[x] = newe++;
}
inline int path(int u)
{
    for(int i = newhead[u]; i != -1; i = newedge[i].next)
    {
        int v =  newedge[i].v;
        if(!mark[v])
        {
            mark[v] = 1;
            if(cy[v] == -1 || path(cy[v]))
            {
                cx[u] = v;
                cy[v] = u;
                return 1;
            }
        }
    }
    return 0;
}
int solve()
{
    int ans = 0;
    memset(cx, -1, sizeof(cx));
    memset(cy, -1, sizeof(cy));
    for(int i = 1; i <= nt; i++)
    {
        memset(mark, 0, sizeof(mark));
        ans += path(i);
    }
    return ans;
}

void readdata()
{
    int t, v;
    for(int i = 1; i <= n; i++)
    {
        t = in();
        while(t--)
        {
            v = in();
            insert(i, v + nt);
            newinsert(i, v + nt);
        }
    }

    solve();
    all = 2 * nt;
    for(int i = 1; i <= nt; i++)
        if(cx[i] == -1)
        {
            ++all;
            cx[i] = all;
            cy[all] = i;
            for(int k = 1; k <= nt; k++)
                insert(k, all);
        }

    for(int j = 1; j <= nt; j++)
        if(cy[j + nt] == -1)
        {
            ++all;
            cy[j + nt] = all;
            cx[all] = j + nt;
            for(int k = 1; k <= nt; k++)
                insert(all, k + nt);
        }
    for(int i = 1; i <= all; i++)
        if(cx[i] != -1)
            insert(cx[i], i);
}
void tarjan(int u)
{
    low[u] = dfn[u] = ++index;
    instack[u] = 1;
    st[++top] = u;
    int v;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(instack[v]) low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        scc++;
        do
        {
            v = st[top--];
            instack[v] = 0;
            fa[v] = scc;
        }while(v != u);
    }
}
void gao()
{
    init();
    readdata();
    for(int i = 1; i <= all; i++)
        if(!dfn[i]) tarjan(i);
    for(int u = 1; u <= n; u++)
    {
        cnt = 0;
        int ans[MAXN];
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(fa[u] == fa[v])
            {
                if(v - nt <= m)
                ans[cnt++] = v - nt;
            }
        }
        sort(ans, ans + cnt);
        out(cnt);
        for(int i = 0; i < cnt; i++)
        {
            putchar(' ');
            out(ans[i]);
        }
        puts("");
    }
}
int main()
{
    int cas = 0;
    int T;
    //freopen("C:/in.txt", "r", stdin);
    //freopen("C:/out2.txt", "w", stdout);
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        nt = max(n, m);
        printf("Case #%d:\n", ++cas);
        gao();
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值