hdu 4685(强连通分量+二分图的完美匹配)

传送门:Problem 4685

https://www.cnblogs.com/violet-acmer/p/9739990.html

参考资料:

  [1]:二分图的最大匹配、完美匹配和匈牙利算法

  [2]:http://www.cnblogs.com/frog112111/p/3387173.html

题意:

  n个王子和m个公主,王子只能和他喜欢的公主结婚,公主可以和所有的王子结婚,输出所有王子可能的结婚对象。

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

分析:

  这题是poj 1904的加强版,poj 1904的王子和公主数是相等的,这里可以不等,且poj 1904给出了一个初始完美匹配,但是这题就要自己求。

  所以只要求出完美匹配之后,就和poj 1904的做法就完全相同了。

  那么怎么求出完美匹配呢?一开始我用多重匹配的匈牙利算法来做,但是怎么做都不对.......看了题解才恍然大悟=_=

  先说几个坑,这题有点奇怪,就是所有王子都可以争着和同一个公主结婚,只要该王子喜欢该公主,感觉公主有点悲哀呀........

  比如:

       2 2

            1 1

            1 1

      

  输出的答案是:

        1 1  

                            1 1  

  而不是    

        1 1          

         0        

  这里就是和poj 1904有点不一样的地方,坑了我好久.........

  求完美匹配:

  先对原图用匈牙利算法做一遍二分图匹配,但是还有可能剩余一些人还没匹配,只要虚拟出一些节点来匹配剩余的点就行了。

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

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

  这样就构成完美匹配了,接下来就是和poj 1904一样了。

AC代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<vector>
  4 #include<cstring>
  5 #include<algorithm>
  6 using namespace std;
  7 #define pb push_back
  8 #define mem(a,b) memset(a,b,sizeof a)
  9 const int maxn=500+500+500+10;
 10 
 11 int n,m;
 12 //===========匈牙利===========
 13 struct Node
 14 {
 15     int matchM[maxn];
 16     int matchW[maxn];
 17     bool check[maxn];
 18     vector<int >edge[maxn];
 19     void Init()
 20     {
 21         mem(matchM,-1);
 22         mem(matchW,-1);
 23         for(int i=0;i < maxn;++i)
 24             edge[i].clear();
 25     }
 26     void addEdge(int u,int v){
 27         edge[u].pb(v);
 28     }
 29     bool Dfs(int u)
 30     {
 31         for(int i=0;i < edge[u].size();++i)
 32         {
 33             int to=edge[u][i];
 34             if(!check[to])
 35             {
 36                 check[to]=true;
 37                 if(matchW[to] == -1 || Dfs(matchW[to]))
 38                 {
 39                     matchW[to]=u;
 40                     matchM[u]=to;
 41                     return true;
 42                 }
 43             }
 44         }
 45         return false;
 46     }
 47     void Hungarian()
 48     {
 49         for(int i=1;i <= n;++i)
 50         {
 51             mem(check,false);
 52             Dfs(i);
 53         }
 54     }
 55 }_match;
 56 //============================
 57 //============SCC=============
 58 int scc[maxn];
 59 bool vis[maxn];
 60 vector<int >vs;
 61 vector<int >edge[maxn],redge[maxn];
 62 void addEdge(int u,int v)
 63 {
 64     edge[u].pb(v);
 65     redge[v].pb(u);
 66 }
 67 void Dfs(int u)
 68 {
 69     vis[u]=true;
 70     for(int i=0;i < edge[u].size();++i)
 71     {
 72         int to=edge[u][i];
 73         if(!vis[to])
 74             Dfs(to);
 75     }
 76     vs.pb(u);
 77 }
 78 void rDfs(int u,int sccId)
 79 {
 80     scc[u]=sccId;
 81     vis[u]=true;
 82     for(int i=0;i < redge[u].size();++i)
 83     {
 84         int to=redge[u][i];
 85         if(!vis[to])
 86             rDfs(to,sccId);
 87     }
 88 }
 89 void Scc()
 90 {
 91     mem(vis,false);
 92     vs.clear();
 93     for(int i=1;i <= n;++i)
 94         if(!vis[i])
 95             Dfs(i);
 96     mem(vis,false);
 97     int sccId=0;
 98     for(int i=vs.size()-1;i >= 0;--i)
 99     {
100         int to=vs[i];
101         if(!vis[to])
102             rDfs(to,++sccId);
103     }
104 }
105 //============================
106 void Init()
107 {
108     _match.Init();
109     for(int i=0;i < maxn;++i)
110         edge[i].clear(),redge[i].clear();
111 }
112 int main()
113 {
114     int T;
115     scanf("%d",&T);
116     for(int kase=1;kase <= T;++kase)
117     {
118         Init();
119         scanf("%d%d",&n,&m);
120         for(int i=1;i <= n;++i)
121         {
122             int k;
123             scanf("%d",&k);
124             while(k--)
125             {
126                 int v;
127                 scanf("%d",&v);
128                 addEdge(i,v+n);
129                 _match.addEdge(i,v+n);
130             }
131         }
132         _match.Hungarian();//匈牙利算法求最大匹配
133         int all=n+m;
134         for(int i=1;i <= n;++i)
135         {
136             if(_match.matchM[i] == -1)//为剩余王子匹配虚拟公主
137             {
138                 all++;
139                 for(int j=1;j <= n;++j)//所有王子都喜欢该虚拟公主
140                     addEdge(j,all);
141                 _match.matchM[i]=all;
142                 _match.matchW[all]=i;
143             }
144         }
145         for(int i=n+1;i <= n+m;++i)
146         {
147             if(_match.matchW[i] == -1)//为剩余公主匹配虚拟王子
148             {
149                 all++;
150                 for(int j=n+1;j <= n+m;++j)//该虚拟王子喜欢所有公主
151                     addEdge(all,j);
152                 _match.matchM[all]=i;
153                 _match.matchW[i]=all;
154             }
155         }
156         for(int i=1;i <= all;++i)
157             if(_match.matchM[i] != -1)//所有与王子匹配的公主建一条边连向王子
158                 addEdge(_match.matchM[i],i);
159         Scc();//求强连通分量
160         printf("Case #%d:
",kase);
161         for(int i=1;i <= n;++i)
162         {
163             int res=0;
164             int ans[maxn];
165             for(int j=0;j < edge[i].size();++j)
166             {
167                 int to=edge[i][j];
168                 if(scc[i] == scc[to] && to <= m+n)//剔除掉虚拟的公主
169                     ans[res++]=to-n;
170             }
171             sort(ans,ans+res);
172             printf("%d",res);
173             for(int i=0;i < res;++i)
174                 printf(" %d",ans[i]);
175             printf("
");
176         }
177     }
178 }
Kosaraju算法
原文地址:https://www.cnblogs.com/violet-acmer/p/9742580.html