• 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;
    }
  • 相关阅读:
    Hdu 1094 A+B for Input-Output Practice (VI)
    Hdu 1091 A+B for Input-Output Practice (III)
    Hdu 1092 A+B for Input-Output Practice (IV)
    Hdu 1087 Super Jumping! Jumping! Jumping!
    scala学习笔记2(类,继承,抽象类)
    scala学习笔记1(表达式)
    把以前写的几个Linux Framebuffer小工具放到github上了,直接去下吧,别找我要了
    一位台湾朋友刚建的一个FTK的论坛,欢迎加入讨论
    Linux高端内存的由来
    read系统调用深度剖析
  • 原文地址:https://www.cnblogs.com/tanhehe/p/3091322.html
Copyright © 2020-2023  润新知