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

 

posted on 2013-10-23 15:29  jumpingfrog0  阅读(2758)  评论(1编辑  收藏  举报

导航