poj 1904(强连通分量+输入输出外挂)

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

题意:有n个王子,每个王子都有k个喜欢的妹子,每个王子只能和喜欢的妹子结婚,大臣给出一个匹配表,每个王子都和一个妹子结婚,但是国王不满意,他要求大臣给他另一个表,每个王子可以和几个妹子结婚,按序号升序输出妹子的编号,这个表应满足所有的王子最终都有妹子和他结婚。

分析:很好的图论题,把强连通分量和完美匹配结合起来了,记得多校的时候看到类似的题目(hdu 4685),但是不会做,还以为是二分匹配=_=

首先建图,如果王子u喜欢妹子v,则建一条边u指向v(u,v),对于大臣给出的初始完美匹配,如果王子u和妹子v结婚,则建一条边v指向u(v,u),然后求强连通分量,

对于每个王子和妹子,如果他们都在同一个强连通分量内,则他们可以结婚。

为什么呢?因为每个王子只能和喜欢的妹子结婚,初始完美匹配中的丈夫和妻子之间有两条方向不同的边可以互达,则同一个强连通分量中的王子数和妹子数一定是相等的,若王子x可以和另外的一个妹子a结婚,妹子a的原配王子y肯定能找到另外一个妹子b结婚,因为如果找不到的话,则x和a必不在同一个强连通分量中。

所以一个王子可以和所有与他同一强连通分量的妹子结婚,而这不会导致同一强连通分量中的其他王子找不到妹子结婚。

好像很绕的样子@_@。。。。。大家在纸上画画图吧

建图的时候王子从1~n编号,妹子从n+1~2*n编号

这一题的数据量挺大的,光是输入输出就会消耗很多时间了,可以用输入输出外挂来加速读入和输出。

不加输入外挂9000+ms

加输入外挂8000+ms

加输入输出外挂500+ms

AC代码:

  1 #include<cstdio>

  2 #include<cstring>

  3 #include<algorithm>

  4 using namespace std;

  5 

  6 const int N=4000+5;

  7 const int M=200000+4000;

  8 struct EDGE{

  9     int v,next;

 10 }edge[M];

 11 int first[N],low[N],dfn[N],sta[M],belong[N],ans[N];

 12 bool instack[N];

 13 int g,cnt,top,scc;

 14 

 15 void AddEdge(int u,int v)

 16 {

 17     edge[g].v=v;

 18     edge[g].next=first[u];

 19     first[u]=g++;

 20 }

 21 int min(int a,int b)

 22 {

 23     return a<b?a:b;

 24 }

 25 void Tarjan(int u)    //求强连通分量

 26 {

 27     int i,v;

 28     low[u]=dfn[u]=++cnt;

 29     sta[++top]=u;

 30     instack[u]=true;

 31     for(i=first[u];i!=-1;i=edge[i].next)

 32     {

 33         v=edge[i].v;

 34         if(!dfn[v])

 35         {

 36             Tarjan(v);

 37             low[u]=min(low[u],low[v]);

 38         }

 39         else if(instack[v])

 40             low[u]=min(low[u],dfn[v]);

 41     }

 42     if(low[u]==dfn[u])

 43     {

 44         scc++;

 45         while(1)

 46         {

 47             v=sta[top--];

 48             instack[v]=false;

 49             belong[v]=scc;    //缩点

 50             if(u==v)

 51                 break;

 52         }

 53     }

 54 }

 55 int Scan()     //输入外挂

 56 {

 57     int res=0,ch,flag=0;

 58     if((ch=getchar())=='-')

 59         flag=1;

 60     else if(ch>='0'&&ch<='9')

 61         res=ch-'0';

 62     while((ch=getchar())>='0'&&ch<='9')

 63         res=res*10+ch-'0';

 64     return flag?-res:res;

 65 }

 66 void Out(int a)    //输出外挂

 67 {

 68     if(a>9)

 69         Out(a/10);

 70     putchar(a%10+'0');

 71 }

 72 int main()

 73 {

 74     int n,i,u,v,k;

 75     while(scanf("%d",&n)!=EOF)

 76     {

 77         g=cnt=top=scc=0;

 78         memset(first,-1,sizeof(first));

 79         memset(dfn,0,sizeof(dfn));

 80         memset(instack,false,sizeof(instack));

 81         for(i=1;i<=n;i++)

 82         {

 83         //    scanf("%d",&k);

 84             k=Scan();

 85             while(k--)

 86             {

 87             //    scanf("%d",&v);

 88                 v=Scan();

 89                 AddEdge(i,v+n);      //王子i喜欢妹子v

 90             }

 91         }

 92         for(i=1;i<=n;i++)

 93         {

 94         //    scanf("%d",&v);

 95             v=Scan();

 96             AddEdge(v+n,i);       //王子i可以和妹子v结婚

 97         }

 98 

 99         for(i=1;i<=2*n;i++)    //求强连通分量

100             if(!dfn[i])

101                 Tarjan(i);

102 

103         for(u=1;u<=n;u++)

104         {

105             int count=0;

106             for(i=first[u];i!=-1;i=edge[i].next)

107             {

108                 v=edge[i].v;

109                 if(belong[u]==belong[v])   //同一个强连通分量

110                     ans[count++]=v-n;

111             }

112             sort(ans,ans+count);

113         //    printf("%d",count);

114             Out(count);

115             for(i=0;i<count;i++)

116             {

117                 //printf(" %d",ans[i]);

118                 putchar(' ');

119                 Out(ans[i]);

120             }

121         //    printf("\n");

122             putchar('\n');

123         }

124     }

125     return 0;

126 }
View Code

 

你可能感兴趣的:(poj)