zoukankan      html  css  js  c++  java
  • POJ 3177&& 3352

      题意:添加一定数目的边,构成无向双连通图

      方法:一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。

    统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

      low[]值相同的表示在一个双连通子图中,所有可以利用这个进行缩点。然后统计度为1的结点的数目就是leaf数

    渣代码: 

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    
    #define REP(i, n)       for(i = 0; i < n; ++i)
    #define FOR(i, L, H)    for(i = L; i <= H; ++i)
    #define FORD(i, H, L)   for(i = H; i >= L; --i)
    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    
    using namespace std;
    
    const int N = 5024;
    
    struct node {
        int to;
        int next;
    } g[N*100];
    
    int head[N];
    int dfn[N];
    int low[N];
    int out[N];
    int t, cnt, top, ind;
    
    void init() {
        CL(head, -1); CL(dfn, 0);
        CL(low, 0); CL(out, 0);
        t = cnt = top = ind = 0;
    }
    
    void add(int u, int v) {
        g[t].to = v; g[t].next = head[u]; head[u] = t++;
    }
    
    void tarjan(int u, int pre) {
        dfn[u] = low[u] = ++ind;
        int i, v;
        bool flag = true;
        for(i = head[u]; i != -1; i = g[i].next) {
            v = g[i].to;
            if(v == pre && flag)    {flag = false; continue;}   //考虑重边
            if(!dfn[v]) {
                tarjan(v, u);
                low[u] = min(low[u], low[v]);
            } else low[u] = min(low[u], dfn[v]);
        }
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int n, r, i, u, v, ans;
        while(~scanf("%d%d", &n, &r)) {
            init();
            while(r--) {
                scanf("%d%d", &u, &v);
                add(u, v); add(v, u);
            }
            FOR(i, 1, n)    if(!dfn[i]) tarjan(i, -1);
            FOR(u, 1, n) {
                for(i = head[u]; i != -1; i = g[i].next) {
                    v = g[i].to;
                    if(low[u] != low[v]) {
                        out[low[u]] ++;
                    }
                }
            }
            ans = 0;
            FOR(i, 1, ind) {
                if(out[i] == 1) ++ans;
            }
            if(ans == 1)     printf("0\n");
            else    printf("%d\n", (ans + 1)/2);
        }
        return 0;
    }

     

     

     

  • 相关阅读:
    React 生命周期
    React 总结
    系统后台设置
    数据库的事务日志已满,起因为"LOG_BACKUP"。
    webpack 打包器
    地图API
    ES6 与 React
    前端流行的技术
    Javascript 函数声明、函数表达式与匿名函数自执行表达式
    Javascript 解读与思想
  • 原文地址:https://www.cnblogs.com/vongang/p/2348344.html
Copyright © 2011-2022 走看看