• POJ 3352&&3177 (割边 && 边双连通分量)


    题目:加几条边才能使原图变成边双连通分量。 [求割边]对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号。定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点。根据定义,则有:Low(u)=Min {DFS(u),DFS(v)|(u,v)为后向边(等价于DFS(v)<DFS(u)且v不为u的父亲节点),Low(v)|(u,v)为树枝边} [条件]一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v) [求点双连通分支]只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。 [注]重边对求割边和边连通分量有影响,此题无重边,重边对求割边和边连通分量有影响,重边处理方法是在DFS时标记每条边及其反向边是否被访问过(即按边访问),而不是判断顶点(按点访问)。 POJ 3352(无重边):
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define MID(x,y) ((x+y)>>1)
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    
    const int MAXV = 5005;
    const int MAXE = 20005;
    struct node{
        int u, v;
        int next;
        int opp;        //把一个无向边拆成两个有向边,对应反向边标号
    }arc[MAXE];
    int cnt, head[MAXV];
    void init(){
        cnt = 0;
        mem(head, -1);
        return ;
    }
    void add(int u, int v){
        arc[cnt].u = u;
        arc[cnt].v = v;
        arc[cnt].next = head[u];
        arc[cnt].opp = cnt + 1;
        head[u] = cnt ++;
        arc[cnt].u = v;
        arc[cnt].v = u;
        arc[cnt].next = head[v];
        arc[cnt].opp = cnt - 1;
        head[v] = cnt ++;
        return ;
    }
    int id, dfn[MAXV], low[MAXV];
    int bridge[MAXE];                //标记该边是不是桥
    void tarjan(int u, int father){
        dfn[u] = low[u] = ++id;
        for (int i = head[u]; i != -1; i = arc[i].next){
            int v = arc[i].v;
            if (v == father)    continue;
            //if (dfn[v] < dfn[u]){
                if (!dfn[v]){
                    tarjan(v, u);
                    low[u] = min(low[u], low[v]);
                    if (dfn[u] < low[v]){
                        bridge[i] = 1;
                        bridge[arc[i].opp] = 1;
                    }
                }
                else{
                    low[u] = min(low[u], dfn[v]);
                }
            //}
        }
    }
    int bcc_num;
    bool vis[MAXV];
    bool vis_arc[MAXE];             //一条边无向边(两个有向边)只访问一次,
    int bcc[MAXV];                  //标记点属于哪个边双连通分支
    int deg[MAXV];                  //缩点后的新图节点度数,便于计算叶子结点数
    void fill(int u){
        vis[u] = 1;
        bcc[u] = bcc_num;
        //cout  << u << " " << bcc[u] << endl;
        for (int i = head[u]; i != -1; i = arc[i].next){
            if (bridge[i])  continue;
            int v = arc[i].v;
            if (!vis[v])
                fill(v);
        }
    }
    int find_bcc(int n){
        mem(vis, 0);
        mem(deg, 0);
        //确定每个点所属边双联通分量
        for (int i = 1; i <= n; i ++){
            if (!vis[i]){
                ++ bcc_num;
                fill(i);
            }
        }
        mem(vis_arc, 0);
        //计算还需连几条边才能构成双联通图
        for (int i = 0; i < cnt; i ++){
            if (!vis_arc[i]){
            //if(bridge[i]){
                vis_arc[i] = vis_arc[arc[i].opp] = 1;
                int u = arc[i].u;
                int v = arc[i].v;
                if (bcc[u] != bcc[v]){
                    deg[bcc[u]] ++;
                    deg[bcc[v]] ++;
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= bcc_num; i ++){
            if (deg[i] == 1)
                res ++;
        }
        return (res+1)/2;
    }
    void solve(int n){
        id = bcc_num =0;
        mem(dfn, 0);
        mem(low, 0);
        mem(bridge, 0);
        for (int i = 1; i <= n; i ++){
            if (!dfn[i])
                tarjan(1, 0);
        }
        printf("%d\n", find_bcc(n));
        return ;
    }
    int main(){
        int F,R;
        while(scanf("%d %d", &F, &R) != EOF){
            init();
            for (int i = 0; i < R; i ++){
                int u, v;
                scanf("%d %d", &u, &v);
                add(u, v);
            }
            solve(F);
        }
    	return 0;
    }
    
      POJ 3177 (有重边):
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define MID(x,y) ((x+y)>>1)
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    
    const int MAXV = 5005;
    const int MAXE = 20005;
    struct node{
        int u, v;
        int next;
        int opp;        //把一个无向边拆成两个有向边,对应反向边标号
    }arc[MAXE];
    int cnt, head[MAXV];
    void init(){
        cnt = 0;
        mem(head, -1);
        return ;
    }
    void add(int u, int v){
        arc[cnt].u = u;
        arc[cnt].v = v;
        arc[cnt].next = head[u];
        arc[cnt].opp = cnt + 1;
        head[u] = cnt ++;
        arc[cnt].u = v;
        arc[cnt].v = u;
        arc[cnt].next = head[v];
        arc[cnt].opp = cnt - 1;
        head[v] = cnt ++;
        return ;
    }
    int id, dfn[MAXV], low[MAXV];
    int bridge[MAXE];                //标记该边是不是桥
    bool vis_arc[MAXE];             //一条边无向边(两个有向边)只访问一次,
    void tarjan(int u, int father){
        dfn[u] = low[u] = ++id;
        for (int i = head[u]; i != -1; i = arc[i].next){
            int v = arc[i].v;
            if (vis_arc[i]) continue;
            vis_arc[i] = vis_arc[arc[i].opp] = 1;
            if (!dfn[v]){
                tarjan(v, u);
                low[u] = min(low[u], low[v]);
                if (dfn[u] < low[v]){
                    bridge[i] = bridge[arc[i].opp] = 1;
                }
            }
            else{
                low[u] = min(low[u], dfn[v]);
            }
        }
    }
    int bcc_num;
    bool vis[MAXV];
    int bcc[MAXV];                  //标记点属于哪个边双连通分支
    int deg[MAXV];                  //缩点后的新图节点度数,便于计算叶子结点数
    void fill(int u){
        vis[u] = 1;
        bcc[u] = bcc_num;
        //cout << u << " " << bcc[u] << endl;
        for (int i = head[u]; i != -1; i = arc[i].next){
            if (bridge[i])  continue;
            int v = arc[i].v;
            if (!vis[v])
                fill(v);
        }
    }
    int find_bcc(int n){
        mem(vis, 0);
        mem(deg, 0);
        //确定每个点所属边双联通分量
        for (int i = 1; i <= n; i ++){
            if (!vis[i]){
                ++ bcc_num;
                fill(i);
            }
        }
        mem(vis_arc, 0);
        //计算还需连几条边才能构成双联通图
        for (int i = 0; i < cnt; i ++){
            if (!vis_arc[i]){
                vis_arc[i] = vis_arc[arc[i].opp] = 1;
                int u = arc[i].u;
                int v = arc[i].v;
                if (bcc[u] != bcc[v]){
                    deg[bcc[u]] ++;
                    deg[bcc[v]] ++;
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= bcc_num; i ++){
            if (deg[i] == 1)
                res ++;
        }
        return (res+1)/2;
    }
    void solve(int n){
        id = bcc_num =0;
        mem(dfn, 0);
        mem(low, 0);
        mem(bridge, 0);
        mem(vis_arc, 0);
        for (int i = 1; i <= n; i ++){
            if (!dfn[i])
                tarjan(1, 0);
        }
        printf("%d\n", find_bcc(n));
        return ;
    }
    int main(){
        int F,R;
        while(scanf("%d %d", &F, &R) != EOF){
            init();
            for (int i = 0; i < R; i ++){
                int u, v;
                scanf("%d %d", &u, &v);
                add(u, v);
            }
            solve(F);
        }
    	return 0;
    }
    
    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    layui timeline使用
    Java随机数使用
    Matlab信号处理工具箱函数
    高斯白噪声(white Gaussian noise,WGN)
    概率分布之间的距离度量以及python实现
    Wasserstein距离 和 Lipschitz连续
    线性分式变换(linear fractional transformation)
    算法的时间复杂度和空间复杂度-总结
    随机森林(Random Forest,简称RF)
    增强学习与马尔科夫决策过程
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114027.html
Copyright © 2020-2023  润新知