POJ1904-King's Quest(思维+强连通缩点)

7 篇文章 0 订阅
2 篇文章 0 订阅

King’s Quest
Time Limit: 15000MS Memory Limit: 65536K
Total Submissions: 9590 Accepted: 3551
Case Time Limit: 2000MS
Description

Once upon a time there lived a king and he had N sons. And there were N beautiful girls in the kingdom and the king knew about each of his sons which of those girls he did like. The sons of the king were young and light-headed, so it was possible for one son to like several girls.

So the king asked his wizard to find for each of his sons the girl he liked, so that he could marry her. And the king’s wizard did it – for each son the girl that he could marry was chosen, so that he liked this girl and, of course, each beautiful girl had to marry only one of the king’s sons.

However, the king looked at the list and said: “I like the list you have made, but I am not completely satisfied. For each son I would like to know all the girls that he can marry. Of course, after he marries any of those girls, for each other son you must still be able to choose the girl he likes to marry.”

The problem the king wanted the wizard to solve had become too hard for him. You must save wizard’s head by solving this problem.
Input

The first line of the input contains N – the number of king’s sons (1 <= N <= 2000). Next N lines for each of king’s sons contain the list of the girls he likes: first Ki – the number of those girls, and then Ki different integer numbers, ranging from 1 to N denoting the girls. The sum of all Ki does not exceed 200000.

The last line of the case contains the original list the wizard had made – N different integer numbers: for each son the number of the girl he would marry in compliance with this list. It is guaranteed that the list is correct, that is, each son likes the girl he must marry according to this list.

Output

Output N lines.For each king’s son first print Li – the number of different girls he likes and can marry so that after his marriage it is possible to marry each of the other king’s sons. After that print Li different integer numbers denoting those girls, in ascending order.
Sample Input

4
2 1 2
2 1 2
2 2 3
2 3 4
1 2 3 4
Sample Output

2 1 2
2 1 2
1 3
1 4
题目:PO1904
题意:国王有n个王子,他们都有一些喜欢的美女,为了使每个王子都可以找到喜欢美女结婚,国王找来女巫列出了一份包含一个使每个王子都满足的配对名单,现在问在满足所有王子都可以以和喜欢的美女结婚的条件下,每个王子可以选择那些美女结婚。
思路:拿到题,感觉是个二分匹配,但注意n有2000,边有200000条,如果对每条边都判断一下是否可以完美匹配的话,想想都恐怖。并且这样的话,题目中给的匹配貌似没什么用。所以事情没有这么简单。
首先,我们再读一遍题,发现有n个王子和n个美女,并且存在一个完美的n的匹配。题目这样给,自然有他的用意。
我们想一下,假设题目中给的匹配是x1(王子)-y1(美女),x2-y2….xn-yn,那么我们要找到一个另一个完美匹配,现在我们让x1-y2(假设存在关系),那么在x2~xn中必定会有一个王子和y1匹配
现在我们建一个图,我们从王子向每一个喜欢的美女建一条边,并在原始匹配中从美女向王子建边:
这里写图片描述
图中圆圈表示美女,无框数字为王子黑色表示原始配对,黄色表示王子喜欢的其他美女,红色箭头为初始匹配中美女到王子的边。
从图中我们看绿色的箭头,如果x1不与y1配对,那么绿色箭头沿黄色路径指向y2,表示x1-y2,绿色箭头由y2指向x2则表示断开原来的匹配,这时x2-y1,即一次成功的配对。
在x1,x2,y1,y2这个集合中,x1即使不走x1–y1这条路也可以沿路径到达集合中任意一个点。事实上,这个集合中任何一个点都满足这个性质。
巧合吗?这刚好是一个强联通分量。
我们回想一下刚才的建模方式,如果x1不走原来的黑线(原始匹配线),而选择一条其他的可能线(黄线),连接(由x指向y即为连接),被指的yi由原始匹配线(黑线)返回原始匹配对象(断开匹配,y指向x即为断开),被断开匹配的xi再由黄线寻找其他匹配对象……沿这个规则走下去,由于这是一个n的匹配最终总会走回y1,加上y1-x1这条边,就形成了一个回路,遍历过的所有点都可以沿这个环到达其他的任何一个遍历过的点
显然,我们得到结论:xi可以和其所在强联通分量中的任何一个y匹配,并且不影响其他x的匹配。现在,我们只需要找到xi所在的强联通分量,遍历其中的y,就可以得到答案啦。
AC代码:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#define met(s,k) memset(s,k,sizeof s)
#define scan(a) scanf("%d",&a)
#define scanl(a) scanf("%lld",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannl(a,b) scanf("%lld%lld",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define prin(a) printf("%d\n",a)
#define prinl(a) printf("%lld\n",a)
using namespace std;
typedef long long ll;
const int maxn=10000;
const int inf=2100;
const double eps=1e-4;
struct edge
{
    int en,next;
} e[400000];
int n,m,num,p[maxn],dfn[maxn],dep[maxn],vis[maxn],lid,stacks[maxn],cont,kc,color[maxn],anss,ans[maxn];
bool cmp(int a,int b)
{
    return a<b;
}
void init()
{
    met(p,-1);
    num=0;
    met(dfn,0);
    met(dep,0);
    met(vis,0);
    met(color,0);
    kc=1;
    lid=-1;
    cont=1;
}
void add(int st,int en)
{
    e[num].en=en;
    e[num].next=p[st];
    p[st]=num++;
}
void tarjan(int u)//缩点
{
    dfn[u]=dep[u]=cont++;
    stacks[++lid]=u;
    vis[u]=1;
    for(int i=p[u]; i!=-1; i=e[i].next)
    {
        int v=e[i].en;
        if(vis[v]==0)tarjan(v);
        if(vis[v]==1)dep[u]=min(dep[u],dep[v]);
    }
    if(dfn[u]==dep[u])
    {
        kc++;
        do
        {
            vis[stacks[lid]]=-1;
            color[stacks[lid]]=kc;
        }
        while(stacks[lid--]!=u);
    }
}
int main()
{
    while(~scan(n))
    {
        init();
        for(int i=1; i<=n; i++)
        {
            scan(m);
            for(int j=0; j<m; j++)
            {
                int x;
                scan(x);
                add(i,x+n);//美女存虚拟点,建边
            }
        }
        for(int i=1; i<=n; i++)
        {
            int x;
            scan(x);
            add(x+n,i);//原始匹配向x建边
        }
        for(int i=1; i<=n*2; i++)
        {
            if(!vis[i])tarjan(i);//寻找强联通分量
        }
        for(int i=1; i<=n; i++)
        {
            anss=0;
            for(int j=p[i];j!=-1;j=e[j].next)
            {
                int v=e[j].en;
                if(color[i]==color[v])//color表示缩点后,每个点的颜色,在同一分量中的点颜色相同
                {
                    ans[anss++]=v-n;
                }
            }
            sort(ans,ans+anss);//题目要求对美女编号排序
            printf("%d",anss);
            for(int j=0;j<anss;j++)printf(" %d",ans[j]);
            printf("\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值