King's Quest 【POJ - 1904】【完美匹配的替代方案(Tarjan强联通)】

题目链接


题记 —— 完美匹配:二分图的左右两边的所有结点都能得到对面的结点的匹配,并且每个点仅可使用一次。

  题意:有N个王子,N个公主,每个王子都有Ki个自己喜欢的公主,现在国师给他们配对,让每个王子娶到自己的某个心仪的姑娘。但是,王子们的父亲,也就是国王,并不满足于此,他想知道,每个王子最多可以选择哪几个公主中的一个来成亲,使得其他的王子还能娶到自己心仪的姑娘中的一个。简而言之,某个王子有X个可供选择的公主(一定是他喜欢的公主,但是不代表他喜欢的公主就一定是可供选择的),他任意选择X中的一个,其他的N-1个王子仍然是可以选择某个他喜欢的公主来成亲的,最后N个王子都是可以成功娶走自己喜欢的某个公主。

  简单的说,二分图的二部各自各一个顶点匹配掉之后,剩下的2*(N - 1)个结点仍然能构成完美匹配,我们想知道X部的每个点可供链接的最多的Y部的点的数量,以及点。(升序输出)

  思路

  首先,我们知道,目前的方案,此时国师给出的方案是一定是可行的,也就是用国师的方案就一定是会构成一个完美匹配的。

  再往下,我们可以根据匹配的性质,也就是一个寻找增广的过程:

但是,我们访问到3的时候,其实我们返回b,找到2,发现2可以换成a,于是形成了增广路,3 -> b -> 2 -> a的这样的过程。

  所以,我们返回到原来的问题,如果我们形成了增广环,是不是代表了这个环内的每个匹配都是可以相互交换的。又因为一开始是给出的完美匹配,所以这样交换过后得到的仍然是完美匹配,它不会改变匹配的大小。

  所以,我们可以利用强联通去寻找环,在环内的点,如果跟王子是有匹配边的(也就是王子喜欢的),我们就可以认为他们是可以选择的,他们的选择配偶是不会干涉到其他王子公主的选择的。

  最后,做法就是先是王子连到公主的有向边,然后呢,由于给出了完美匹配,所以呢,再由公主连回王子,这条边实际上在网络流中就是因为匹配,所以生成的反向边了(你a -> b的流流完了,就会变成b -> a有流了)。所以,最后如果是形成了环的话,那么环内的王子喜欢的公主,就是可供该王子挑选了,不会影响到其他王子的婚配。

  类似的问题,我们可以举一反三,当不给出反向边的时候,我们甚至可以利用匈牙利算法来进行跑这样的完美匹配,知道二分图的某个部上的某个点是由对面的哪个点得来的,我们一样可以利用它进行环的生成。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
//#define INF 10000007.
#define eps 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 4e3 + 7, maxM = 2e5 + 7;
int N, cnt, head[maxN];
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxM + maxN];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
int dfn[maxN], tot, low[maxN], Belong[maxN], Bcnt, Stap[maxN], Stop;
vector<int> vt[maxN];
deque<int> ans;
bool like[maxN] = {false};
bool instack[maxN];
void Tarjan(int u)
{
    dfn[u] = low[u] = ++tot;
    Stap[++Stop] = u;
    instack[u] = true;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        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])
    {
        int v; Bcnt++; vt[Bcnt].clear();
        do
        {
            v = Stap[Stop--];
            Belong[v] = Bcnt;
            instack[v] = false;
            if(v > N) vt[Bcnt].push_back(v - N);
        } while(u ^ v);
        sort(vt[Bcnt].begin(), vt[Bcnt].end());
    }
}
inline void init()
{
    cnt = tot = Stop = Bcnt = 0;
    for(int i=(N << 1); i; i--)
    {
        head[i] = -1;
        dfn[i] = 0;
        instack[i] = false;
    }
}
int main()
{
    while(scanf("%d", &N) != EOF)
    {
        init();
        for(int i=1, ki; i<=N; i++)
        {
            scanf("%d", &ki);
            for(int j=1, v; j<=ki; j++)
            {
                scanf("%d", &v);
                addEddge(i, v + N);
            }
        }
        for(int i=1, u; i<=N; i++)  //每个王子原来娶哪个公主
        {
            scanf("%d", &u);    //是公主啊啊啊
            addEddge(u + N, i);
        }
        for(int i=1; i<=N; i++) if(!dfn[i]) Tarjan(i);
        for(int u=1, len, id; u<=N; u++)
        {
            ans.clear();
            for(int i=head[u], v; ~i; i=edge[i].nex)
            {
                v = edge[i].to;
                like[v - N] = true;
            }
            id = Belong[u];
            len = (int)vt[id].size();
            for(int j=0; j<len; j++) if(like[vt[id][j]]) ans.push_back(vt[id][j]);
            len = (int)ans.size();
            printf("%d", len);
            for(int j=0; j<len; j++) printf(" %d", ans[j]);
            printf("\n");
            for(int i=head[u], v; ~i; i=edge[i].nex)
            {
                v = edge[i].to;
                like[v - N] = false;
            }
        }
    }
    return 0;
}

 

写在最后,看完的同学们,祝大年初一,新年快乐啦!!!

美丽的人生,伴随着新年的到来增添了更美丽的色彩,愿你的明天像梦想那般绚丽多姿!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值