• 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('
    ');
    84         }
    85     }
    86     return 0;
    87 }
  • 相关阅读:
    arm-linux-gcc 安装和测试
    JIT动态编译器的原理与实现之Interpreter3
    sql server常有的问题-实时错误'91' 对象变量或with块变量未设置
    WM_PARENTNOTIFY的作用(不完全)
    WM_CLOSE、WM_DESTROY、WM_QUIT学习总结(点击关闭按钮会触发WM_CLOSE消息,DestroyWindow API会触发WM_DESTROY和WM_NCDESTROY消息,MSDN上写的很清楚)
    Windows XP 每次开机都自动检测硬盘 解决办法(可以用HDDRegenerate修复坏道)
    这141家创业公司为什么失败
    QTableView 固定列宽度(鼠标拖动后,仍可固定)
    银行快捷支付的思考
    QQ空间如何显示相片
  • 原文地址:https://www.cnblogs.com/WABoss/p/5656714.html
Copyright © 2020-2023  润新知