zoukankan      html  css  js  c++  java
  • kuangbin-连通图

     小结:

    1,tarjan求有向图强联通请注意处理回退边时,只能把还在栈中元素更新。
    2,有重边时,注意不要传father,要传非相邻边。
    3,

    A - Network of Schools

     POJ - 1236 

    两个问题:有向图,最小点覆盖,最小边使得图联通。

    最小点覆盖 :即为入度为0强联通个数。

    最小边 : 

    图中入度为0的强联通数为 a ,出度为0的强联通为 b 。那么让出度为0 分量去 连 入度为 0 的分量一定是最优的,

    即max( a, b )

    但要注意强联通算法的更新low值。

    #include<cstdio>
    #include<iostream>
    #include<stack>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    // const ll MOD=998244353;
    const int N=1e5+5;
    int dfn,n,ecnt,SCC;
    int sccno[N],num[N],low[N],belong[N],in[N],out[N],head[N];
    bool instack[N];
    struct edge{int v,next;}e[N];
    void add(int u,int v){
    e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    stack<int>sta;
    void init(){
        ecnt=SCC=dfn=0;
        memset(head,-1,sizeof head);
        memset(in,0,sizeof in);
        memset(out,0,sizeof out);
        memset(belong,0,sizeof belong);
        memset(low,0,sizeof low);
        memset(num,0,sizeof num);
        memset(sccno,0,sizeof sccno);
        while(!sta.empty())sta.pop();
    }
    void tarjan(int u){
        num[u]=low[u]=++dfn;
        sta.push(u);
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(!num[v]){
                tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(!belong[v])low[u]=min(low[u],num[v]); 
        }
        if(num[u]==low[u]){
        SCC++;
        while(1){
            int v=sta.top();sta.pop();
            belong[v]=SCC;
            if(v==u)break;
            }
        }
    }
    void solve(){
        for(int i=1;i<=n;i++)if(!num[i])tarjan(i);
        for(int u=1;u<=n;u++){
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].v;
                if(belong[u]!=belong[v]){
                    out[ belong[u] ]++;
                    in[ belong[v] ]++;
                }
            }
        }
        int a=0,b=0;
        for(int i=1;i<=SCC;i++){
        if(in[i]==0)a++;
        else if(out[i]==0)b++;
        }
        if(SCC==1)cout<<1<<endl<<0<<endl;
        else cout<<a<<endl<<max(a,b)<<endl;
    }
    int main(){
        init();
        scanf("%d",&n);
        for(int u=1,v;u<=n;u++){
            while(~scanf("%d",&v),v){
            add(u,v);
            }
        }
        solve();
        system("pause");
        return 0;
    }
    View Code

     

    D - Network

     POJ - 3694 

    动态求SCC个数。

    对于一张图,跑一遍tarjan,生成一颗树,树边一定是割边,当新加入边时,那么 u ,v 到 lca 的边都变成非桥,暴力更新即可。

    听说压点更快,少了在一个强联通的时间。

    #include<cstdio>
    #include<iostream>
    #include<stack>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll MOD=998244353;
    const int N=1e5+5;
    int dfn,n,m,ecnt,SCC;
    struct edge{int v,next;}e[N*10];
    int head[N],low[N],num[N],dep[N],bridge[N],pre[N];
    void init(){
        memset(head,-1,sizeof head);
        memset(low,0,sizeof low);
        memset(num,0,sizeof num);
        memset(dep,0,sizeof dep);
        memset(bridge,0,sizeof bridge);
        memset(pre,0,sizeof pre);
        SCC=ecnt=dfn=0;
    }
    void add(int u,int v){
    e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    void tarjan(int u,int fa){
        low[u]=num[u]=++dfn;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==fa)continue;
            if(!num[v]){
                dep[v]=dep[u]+1;
                pre[v]=u;
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>num[u])bridge[v]=1,SCC++;
            }
            else low[u]=min(low[u],num[v]);
        }
    }
    void work(int u,int v){
        while(dep[u]>dep[v]){
        if(bridge[u]){bridge[u]=0,SCC--;}
        u=pre[u];
        }
        while(dep[u]<dep[v]){
        if(bridge[v]){bridge[v]=0,SCC--;}
        v=pre[v];
        }
        while(u!=v){
        if(bridge[u]){SCC--;bridge[u]=0;}
        if(bridge[v]){SCC--;bridge[v]=0;}
        u=pre[u];
        v=pre[v];
        }
                printf("%d
    ",SCC);
    }
    int main(){
        int kase=0;
        while(~scanf("%d %d",&n,&m),n+m){
            init();
            for(int i=1,u,v;i<=m;i++){
                scanf("%d %d",&u,&v);
                add(u,v);add(v,u);
            }
            tarjan(1,-1);
            int u,v,q;scanf("%d",&q);
            if(kase)puts("");
            printf("Case %d:
    ",++kase);
            while(q--){
                scanf("%d %d",&u,&v);
                // add(u,v);add(v,u);
                work(u,v);
            }
    
        }
        // system("pause");
        return 0;
    }
    View Code

    E - Redundant Paths

     POJ - 3177

    题意:给你一张图,新增加一些边,使得图成为边双联通,但是图有重边,
    解法:tarjan一遍,注意处理回退边时只能传相邻边,传father就gg ,因为有重边,
    然后统计叶子节点个数,连边解决。

    #include<cstdio>
    #include<iostream>
    #include<stack>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll MOD=998244353;
    const int N=1e5+5;
    int top=0,dfn,n,m,ecnt,SCC;
    struct edge{int v,next;}e[N*10];
    int head[N],low[N],num[N],out[N],in[N],belong[N];
    // stack<int>stk;
    int stk[N];
    void init(){
        memset(head,-1,sizeof head);
        memset(low,0,sizeof low);
        memset(num,0,sizeof num);
        memset(belong,0,sizeof belong);
        memset(in,0,sizeof in);
        memset(out,0,sizeof out);
        SCC=ecnt=dfn=top=0;
        // while(!stk.empty())stk.pop();
    }
    void add(int u,int v){
    e[ecnt].v=v;e[ecnt].next=head[u];head[u]=ecnt++;
    }
    void tarjan(int u,int fa){
        num[u]=low[u]=++dfn;
        // stk.push(u);
        stk[++top]=u;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if( (i^1) ==fa)continue;
            if(!num[v]){
                tarjan(v,i);
                low[u]=min(low[u],low[v]);
            }
            else if(!belong[v])low[u]=min(low[u],num[v]); 
        }
        if(num[u]==low[u]){
          SCC++;
        while(1){
            int v=stk[top];top--;
            // int v=stk.top();stk.pop();
            belong[v]=SCC;
            if(v==u)break;
            }
        }
    }
    int main(){
            // while(scanf("%d %d",&n,&m)){    
            scanf("%d %d",&n,&m);
            init();
            for(int i=1,u,v;i<=m;i++){
                scanf("%d %d",&u,&v);
                add(u,v);add(v,u);
            }
    
            for(int i=1;i<=n;i++)if(!num[i])tarjan(i,-1);
            for(int u=1;u<=n;u++){
                for(int i=head[u];~i;i=e[i].next){
                    int v=e[i].v;
                    if(belong[u]!=belong[v]){
                        out[ belong[u] ]++;
                        in[ belong[v] ]++;
                    }
                }
            }
        int ans=0;
        for(int i=1;i<=SCC;i++)if(in[i]==1)ans++;
            ans=(ans+1)/2;
        printf("%d
    ",ans);
        // }
        system("pause");
        return 0;
    }
    // 题意:给你一张图,新增加一些边,使得图成为边双联通,但是图有重边,
    // 解法:tarjan一遍,注意处理回退边时只能传相邻边,传father就gg ,因为有重边,
    // 然后统计叶子节点个数,连边解决。
    View Code

     

    H - Prince and Princess

     HDU - 4685 

    题意:就是让你求,保证最大的二分图匹配的,每个点的匹配情况。

    想的太多,做的太少;
  • 相关阅读:
    koa2环境搭建
    单例模式
    nodejs fs path
    path node
    webpack code splitting
    babel 插件编写
    C#验证码类
    C#身份证识别相关技术
    C# Socket服务端与客户端通信(包含大文件的断点传输)
    动态抓取网页信息
  • 原文地址:https://www.cnblogs.com/littlerita/p/12677139.html
Copyright © 2011-2022 走看看