zoukankan      html  css  js  c++  java
  • hdu 4612 Warm up(无向图Tarjan+树的直径)

    题意:有N个点,M条边(有重边)的无向图,这样图中会可能有桥,问加一条边后,使桥最少,求该桥树。

    思路:这个标准想法很好想到,缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的两端点连一边,则图中的桥会最少。
    从这题中学到两点,所以写一下解题报告。
    1.官方说judge的栈小,得手动增栈 #pragma comment(linker,"/STACK:102400000,102400000") 以前没见过,算是学习了。
    2.对改正了对Tarjan算法的一个错误理解,以前看某人博客说,无向图中,Tarjan后low值相等的点属于同一块,以前这样判断过,也过了挺多题。但跟别人讨论后发现是错的。。。。。

    3.以前没用Tarjan写过无向图,不懂正向边访问过,标志反向边,这次学习了。


    //937MS    41304K
    #pragma comment(linker, "/STACK:102400000,102400000")
    #include
    #include
    const int VM = 200005;
    const int EM = 1000005;
    
    struct Edeg
    {
        int to,nxt,vis;
    }edge[EM<<1],tree[EM<<1];
    
    int head[VM],vis[VM],thead[VM];
    int dfn[VM],low[VM],stack[VM],belong[VM];
    int ep,bridge,son,maxn,src,n,cnt,scc,top;
    
    int max (int a,int b)
    {
        return a > b ? a : b;
    }
    int min(int a ,int b)
    {
        return a > b ? b : a;
    }
    void addedge (int cu,int cv)
    {
        edge[ep].to = cv;
        edge[ep].vis = 0;
        edge[ep].nxt = head[cu];
        head[cu] = ep ++;
        edge[ep].to = cu;
        edge[ep].vis = 0;
        edge[ep].nxt = head[cv];
        head[cv] = ep ++;
    }
    void Buildtree(int cu,int cv)
    {
        tree[son].to = cv;
        tree[son].nxt = thead[cu];
        thead[cu] = son ++;
    }
    void Tarjan (int u)
    {
        int v;
        vis[u] = 1;
        dfn[u] = low[u] = ++cnt;
        stack[top++] = u;
        for (int i = head[u];i != -1;i = edge[i].nxt)
        {
            v = edge[i].to;
            if (edge[i].vis) continue; //
            edge[i].vis = edge[i^1].vis = 1; //正向边访问过了,反向边得标志,否则两点会成一块。
            if (vis[v] == 1)
                low[u] = min(low[u],dfn[v]);
            if (!vis[v])
            {
                Tarjan (v);
                low[u] = min(low[u],low[v]);
                if (low[v] > dfn[u])
                    bridge ++;
            }
        }
        if (dfn[u] == low[u])
        {
            ++scc;
            do{
                v = stack[--top];
                vis[v] = 0;
                belong[v] = scc;
            }while (u != v);
        }
    }
    void BFS(int u)
    {
        int que[VM+100];
        int front ,rear,i,v;
        front = rear = 0;
        memset (vis,0,sizeof(vis));
        que[rear++] = u;
        vis[u] = 1;
        while (front != rear)
        {
            u = que[front ++];
            front = front % (n+1);
            for (i = thead[u];i != -1;i = tree[i].nxt)
            {
                v = tree[i].to;
                if (vis[v]) continue;
                vis[v] = 1;
                que[rear++] = v;
                rear = rear%(n+1);
            }
        }
        src = que[--rear];//求出其中一个端点
    }
    void DFS (int u,int dep)
    {
        maxn = max (maxn,dep);
        vis[u] = 1;
        for (int i = thead[u]; i != -1; i = tree[i].nxt)
        {
            int v = tree[i].to;
            if (!vis[v])
                DFS (v,dep+1);
        }
    }
    void solve()
    {
        int u,v;
        memset (vis,0,sizeof(vis));
        cnt = bridge = scc = top = 0;
        Tarjan (1);
        memset (thead,-1,sizeof(thead));
        son = 0;
        for (u = 1;u <= n;u ++)              //重构图
            for (int i = head[u];i != -1;i = edge[i].nxt)
            {
                v = edge[i].to;
                if (belong[u]!=belong[v])
                {
                    Buildtree (belong[u],belong[v]);
                    Buildtree (belong[v],belong[u]);
                }
            }
        maxn = 0;    //最长直径
        BFS(1);      //求树直径的一个端点
        memset (vis,0,sizeof(vis));
        DFS(src,0); //求树的最长直径
        printf ("%d
    ",bridge-maxn);
    }
    
    int main ()
    {
        #ifdef LOCAL
            freopen ("in.txt","r",stdin);
        #endif
        int m,u,v;
        while (~scanf ("%d%d",&n,&m))
        {
            if (n == 0&&m == 0)
                break;
            memset (head,-1,sizeof(head));
            ep = 0;
            while (m --)
            {
                scanf ("%d%d",&u,&v);
                addedge (u,v);
            }
            solve();
        }
        return 0;
    }
    


  • 相关阅读:
    【docker】更换挂载目录
    【设计】交互走查表
    MySQL常用字符串函数
    VIM_manual
    MySQL操作符
    基础SELECT实例
    MySQL字符集及校对规则的理解
    Linux命令之tar-rsync
    Linux-PATH_环境变量
    MySQL常用数据类型
  • 原文地址:https://www.cnblogs.com/riskyer/p/3221716.html
Copyright © 2011-2022 走看看