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 }