POJ1904 King's Quest(完备匹配可行边:强连通分量)

题目大概就是说给一张二分图以及它的一个完备匹配,现在问X部的各个点可以与Y部那些些点匹配,使得X部其余点都能找到完备匹配。

枚举然后匹配,当然不行,会超时。

这题的解法是,在二分图基础上建一个有向图:原二分图中边(x,y)连<x,y>的弧,对于那个已知的匹配中的所有边(x,y)连<y,x>的弧,然后对于X部各个点x如果它到Y部的y点有直接的边且它们在同一个强连通分量,那么x就能和y匹配。

我对这个解法的理解是这样的,类似于匈牙利算法的增广路:

  • 如果x和y就属于给定的那个完备匹配那它们本来就在强连通分量上;
  • 否则,就在原匹配的基础上在构造的有向图上走,看能否找到一条“增广路”(其实不是增广路)去修正,看能否使得x匹配的点改成y——

x走向y,这条边必定不属于原匹配,那么这条边加入匹配集合

y走向x1,这条边必定属于原匹配,那么这条边从匹配集合中删去

x1走向y1,这条边必定不属于原匹配,那么这条边加入匹配集合

…… …… ……

最后如果就存在一个yn走能向x,这条边必定属于原匹配,删去;而删除这条边后,就没有和最开头加入匹配集合的那条(x,y)的边存在公共点的边了,(x,y)就是一个合法的匹配

感觉这解法很巧。而这二分图上的边最大独立集,性质挺多的,感觉也挺难运用这些性质。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 #define MAXN 4444
 6 #define MAXM 2222*2222
 7 
 8 struct Edge{
 9     int v,next;
10 }edge[MAXM];
11 int NE,head[MAXN];
12 void addEdge(int u,int v){
13     edge[NE].v=v; edge[NE].next=head[u];
14     head[u]=NE++;
15 }
16 
17 int top,stack[MAXN];
18 bool instack[MAXN];
19 int dn,dfn[MAXN],low[MAXN];
20 int bn,belong[MAXN];
21 void tarjan(int u){
22     dfn[u]=low[u]=++dn;
23     stack[++top]=u; instack[u]=1;
24     for(int i=head[u]; i!=-1; i=edge[i].next){
25         int v=edge[i].v;
26         if(dfn[v]==0){
27             tarjan(v);
28             low[u]=min(low[u],low[v]);
29         }else if(instack[v]){
30             low[u]=min(low[u],dfn[v]);
31         }
32     }
33     if(low[u]==dfn[u]){
34         int v; ++bn;
35         do{
36             v=stack[top--];
37             instack[v]=0;
38             belong[v]=bn;
39         }while(u!=v);
40     }
41 }
42 
43 int main(){
44     int n,a,b;
45     while(~scanf("%d",&n)){
46         NE=0;
47         memset(head,-1,sizeof(head));
48         for(int i=1; i<=n; ++i){
49             scanf("%d",&a);
50             while(a--){
51                 scanf("%d",&b);
52                 addEdge(i,b+n);
53             }
54         }
55         for(int i=1; i<=n; ++i){
56             scanf("%d",&a);
57             addEdge(a+n,i);
58         }
59         top=dn=bn=0;
60         memset(dfn,0,sizeof(dfn));
61         memset(instack,0,sizeof(instack));
62         for(int i=1; i<=2*n; ++i){
63             if(dfn[i]==0){
64                 tarjan(i);
65             }
66         }
67         for(int u=1; u<=n; ++u){
68             int cnt=0;
69             bool vis[MAXN]={0};
70             for(int i=head[u]; i!=-1; i=edge[i].next){
71                 int v=edge[i].v;
72                 if(belong[u]==belong[v]){
73                     ++cnt;
74                     vis[v-n]=1;
75                 }
76             }
77             printf("%d",cnt);
78             for(int i=1; i<=n; ++i){
79                 if(vis[i]){
80                     printf(" %d",i);
81                 }
82             }
83             putchar('\n');
84         }
85     }
86     return 0;
87 }

 

posted @ 2016-07-09 21:53  WABoss  阅读(381)  评论(0编辑  收藏  举报