zoukankan      html  css  js  c++  java
  • 牛客小白月赛12 I 华华和月月逛公园 (tarjian 求桥)

    链接:https://ac.nowcoder.com/acm/contest/392/I
    来源:牛客网

    华华和月月逛公园
    时间限制:C/C++ 1秒,其他语言2秒
    空间限制:C/C++ 32768K,其他语言65536K
    64bit IO Format: %lld
    题目描述
    月月和华华一起去逛公园了。公园很大,为了方便,可以抽象的看成一个N个点M条边的无向连通图(点是景点,边是道路)。公园唯一的入口在1号点,月月和华华要从这里出发,并打算参观所有的景点。因为他们感情很好,走多远都不会觉得无聊,所以所有景点和道路都可以无数次的重复经过。月月发现,有些路可走可不走,有些路则必须要走,否则就无法参观所有的景点。现在月月想知道,有几条路是不一定要经过的。因为这是个很正常的公园,所以没有重边和自环。
    输入描述:
    第一行两个正整数N和M,表示点数和边数。
    接下来M行,每行两个正整数U和V表示一条无向边。
    保证给定的图是连通的。
    输出描述:
    输出一行一个非负整数表示不一定要经过的边有几条。
    示例1
    输入
    复制
    5 5
    1 2
    2 3
    3 4
    4 5
    3 5
    输出
    复制
    3
    说明
    例如第三条边,月月和华华可以依次走过第一条、第二条、第五条、第四条边走过全部的景点,所以第三条边不一定要经过。同理还有第四条、第五条边,答案为3。
    备注:
    1le Nle 10^51≤N≤10
    5
    ,1le Mle 3 imes10^51≤M≤3×10
    5

    思路:

    月月发现,有些路可走可不走,有些路则必须要走,否则就无法参观所有的景点。

    无向图中保持联通必须走过的边即为桥,总边数减去桥的个数就是答案,求桥用tarjian算法即可。

    细节见代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <map>
    #include <set>
    #include <vector>
    #include <iomanip>
    #define ALL(x) (x).begin(), (x).end()
    #define sz(a) int(a.size())
    #define all(a) a.begin(), a.end()
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define pii pair<int,int>
    #define pll pair<long long ,long long>
    #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    #define MS0(X) memset((X), 0, sizeof((X)))
    #define MSC0(X) memset((X), '', sizeof((X)))
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define eps 1e-6
    #define gg(x) getInt(&x)
    #define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
    using namespace std;
    typedef long long ll;
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
    ll powmod(ll a, ll b, ll MOD) {ll ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;}
    inline void getInt(int* p);
    const int maxn = 100010;
    const int inf = 0x3f3f3f3f;
    /*** TEMPLATE CODE * * STARTS HERE ***/
    /*
    *  求 无向图的割点和桥
    *  可以找出割点和桥,求删掉每个点后增加的连通块。
    *  需要注意重边的处理,可以先用矩阵存,再转邻接表,或者进行判重
    */
    const int MAXN = maxn;
    const int MAXM = 10 * maxn;
    struct Edge
    {
        int to, next;
        bool cut;//是否为桥的标记
    } edge[MAXM];
    int head[MAXN], tot;
    int Low[MAXN], DFN[MAXN], Stack[MAXN];
    int Index, top;
    bool Instack[MAXN];
    bool cut[MAXN];
    int add_block[MAXN];//删除一个点后增加的连通块
    int bridge;
    
    void addedge(int u, int v)
    {
        edge[tot].to = v; edge[tot].next = head[u]; edge[tot].cut = false;
        head[u] = tot++;
    }
    
    int ans = 0;
    
    void Tarjan(int u, int pre)
    {
        int v;
        Low[u] = DFN[u] = ++Index;
        Stack[top++] = u;
        Instack[u] = true;
        int son = 0;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].to;
            if (v == pre)continue;
            if ( !DFN[v] )
            {
                son++;
                Tarjan(v, u);
                if (Low[u] > Low[v])Low[u] = Low[v];
                //桥
                //一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。
                if (Low[v] > DFN[u])
                {
                    ans++;
                    bridge++;
                    edge[i].cut = true;
                    edge[i ^ 1].cut = true;
                }
                //割点
                //一个顶点u是割点,当且仅当满足(1)或(2) (1) u为树根,且u有多于一个子树。
                //(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,
                //即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)
                if (u != pre && Low[v] >= DFN[u]) //不是树根
                {
                    cut[u] = true;
                    add_block[u]++;
                }
            }
            else if ( Low[u] > DFN[v])
                Low[u] = DFN[v];
        }
        //树根,分支数大于1
        if (u == pre && son > 1)cut[u] = true;
        if (u == pre)add_block[u] = son - 1;
        Instack[u] = false;
        top--;
    }
    
    int n, m;
    void solve(int N)
    {
        memset(DFN, 0, sizeof(DFN));
        memset(Instack, false, sizeof(Instack));
        memset(add_block, 0, sizeof(add_block));
        memset(cut, false, sizeof(cut));
        Index = top = 0;
        bridge = 0;
        for (int i = 1; i <= N; i++)
            if (!DFN[i])
                Tarjan(i, i);
        // for(int i = 1;i <= N;i++)
        //    if(cut[i])
        //       ans++;
        printf("%d
    ", m - ans);
    }
    void init()
    {
        tot = 0;
        memset(head, -1, sizeof(head));
    }
    int main()
    {
        //freopen("D:\common_text\code_stream\in.txt","r",stdin);
        //freopen("D:\common_text\code_stream\out.txt","w",stdout);
    
        init();
        gg(n);
        gg(m);
        repd(i, 1, m)
        {
            int x, y;
            gg(x); gg(y);
            addedge(x, y);
            addedge(y, x);
        }
        solve(n);
    
        return 0;
    }
    
    inline void getInt(int* p) {
        char ch;
        do {
            ch = getchar();
        } while (ch == ' ' || ch == '
    ');
        if (ch == '-') {
            *p = -(getchar() - '0');
            while ((ch = getchar()) >= '0' && ch <= '9') {
                *p = *p * 10 - ch + '0';
            }
        }
        else {
            *p = ch - '0';
            while ((ch = getchar()) >= '0' && ch <= '9') {
                *p = *p * 10 + ch - '0';
            }
        }
    }
    
    
    
    
    
    本博客为本人原创,如需转载,请必须声明博客的源地址。 本人博客地址为:www.cnblogs.com/qieqiemin/ 希望所写的文章对您有帮助。
  • 相关阅读:
    C#开发Activex控件疑难杂症
    spring、struts、mybatis、Postgresql集成使用存储过程进行分页
    C#开发Activex控件升级
    通过Maven将Web程序部署到远程Tomcat8服务器的一些注意事项
    分页存储过程Oracle版
    JSP EL表达式(转)
    关于Log4x
    C#类在初始化时的执行顺序
    使用MSMQ 远程队列
    tomcat部署与Context(转)
  • 原文地址:https://www.cnblogs.com/qieqiemin/p/11420005.html
Copyright © 2011-2022 走看看