zoukankan      html  css  js  c++  java
  • HDU2767 Proving Equivalences(scc)

    题目链接

    题意:

    至少增加几条边,才能让图强连通。

    分析:

    “首先,找出强连通分量,然后把每个强连通分量缩成一个点(缩点),得到一个DAG。 接下来,设有a个结点的入读为0, b个结点的出度为0, 则 max{a, b}就是答案。 注意特殊情况: 当原图已经强连通时, 答案是0而不是1."

    这是《算法竞赛入门经典——训练指南》上的原话。对于证明,搜了一下,没有找到。自己呢,试着画了一下,记下个人心得。

    自己的理解如下;一个含n个点的图,至少要有n条边,才能强连通。即每一个点至少都会有一个入度和出度。对于得到的DAG,设有a个结点的入度为0, b个结点的出度为0,因为增加一条边会同时增加一个入度和一个出度,因此要强连通,即,要想消去所有入度或出度为0的点,至少要 max{a,b}条边。

    对于所述的特殊情况, 当原图已经强连通时,缩点后,整个图会成为一个点,max{a,b} = 1, 但这并不正确,因为本身整个图就强连通,需要0条边。

    AC代码如下:

    #include <iostream>
    #include <vector>
    #include <stack>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    
    const int maxn = 20000+10;
    
    vector<int> G[maxn];
    stack<int> S;
    int pre[maxn], lowlink[maxn], scc_cnt, sccno[maxn], dfs_clock;
    int in0[maxn], out0[maxn];
    
    void dfs(int u){
        pre[u] = lowlink[u] = ++dfs_clock;
        S.push(u);
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(!pre[v]){  
                dfs(v);
                lowlink[u] = min(lowlink[u], lowlink[v]);
            }
            else if(!sccno[v]){ //反向边
                lowlink[u] = min(lowlink[u], pre[v]);
            }
        }
    
        if(lowlink[u] == pre[u]){
            scc_cnt++;
            for(;;){
                int x = S.top(); S.pop();
                sccno[x] = scc_cnt;
                if(x == u) break;
            }
        }
    }
    void find_scc(int n){
        scc_cnt = dfs_clock = 0;
        memset(pre, 0, sizeof(pre));
        memset(sccno, 0, sizeof(sccno));
        for(int i=0; i<n; i++){
            if(!pre[i]) dfs(i);
        }
    }
    
    int main(){
        int n, m, u, v, T;
        scanf("%d", &T);
        while(T--){
            scanf("%d%d", &n, &m);
    
            for(int i=0; i<n; i++) G[i].clear();
            for(int i=0; i<m; i++){
                scanf("%d%d", &u, &v);
                u--; v--;
                G[u].push_back(v);
            }
    
            find_scc(n);
    
            //缩点
            for(int i=1; i<=scc_cnt; i++) in0[i] = out0[i] = 1; //scc_cnt是从1开始编号的
    
            for(int u=0; u<n; u++){
                for(int i=0; i<G[u].size(); i++){
                    int v = G[u][i];
                    if(sccno[u] != sccno[v]) in0[sccno[v]] = out0[sccno[u]] = 0;
                }
            }
    
            int a=0, b=0;
    
            for(int i=1; i<=scc_cnt; i++){
                if(in0[i]) a++;
                if(out0[i]) b++;
            }
    
            int ans = max(a, b);
    
            if(scc_cnt == 1) ans = 0;
            printf("%d\n", ans);
        }
        return 0;
    }
  • 相关阅读:
    vue展开过度动画
    css3,transition,animation两种动画实现区别
    css3动画
    html浏览器高度和宽度和其他dom获取
    afert和b的伪类画三角形
    java导入web项目httpservlet报错
    小程序事件
    Python动态语言的特性
    非关系型数据库之Redis
    深度学习必备基础知识
  • 原文地址:https://www.cnblogs.com/tanhehe/p/3091322.html
Copyright © 2011-2022 走看看