• POJ 1236 学校传数据 强连通+缩点+DAG


    题意描述:

    网络中有一些学校,每个学校可以分发软件给其他学校。可以向哪个分发取决于他们各自维护的一个清单。

    两个问题

    1:至少要copy多少份新软件给那些学校, 才能使得每个学校都能得到。

    2:要在所有的学校的清单里面至少一共增加几项才能 使得把软件给任意一个学校,所有的学校都能收得到。

    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <map>
    #include <algorithm>
    #include <set>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long Ull;
    #define MM(a,b) memset(a,b,sizeof(a));
    const double eps = 1e-10;
    const int  inf =0x7f7f7f7f;
    const double pi=acos(-1);
    const int maxn=100;
    
    vector<int> G[maxn+10];
    int n,m,degin[maxn+10],degout[maxn+10],pre[maxn+10],dfs_clock,scc_cnt,sccno[maxn+10],lowlink[maxn+10];
    stack<int> S;
    
    void tarjan(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])
                {
                    tarjan(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++;
            while(1)
            {
                int x=S.top();S.pop();
                sccno[x]=scc_cnt;
                if(x==u) break;
            }
        }
    }
    
    void find_scc()
    {
        MM(pre,0);
        MM(sccno,0);
        scc_cnt=dfs_clock=0;
        for(int i=1;i<=n;i++)
          if(!pre[i])
            tarjan(i);
    }
    
    int main()
    {
        while(~scanf("%d",&n))
        {
            for(int i=1;i<=n;i++)
                G[i].clear();
    
    
            for(int i=1;i<=n;i++)
                {
                  int u;
                  while(~scanf("%d",&u)&&u)
                     G[i].push_back(u);
                }
    
            MM(degin,0);
            MM(degout,0);
            find_scc();
    
            int in0=0,out0=0;
            for(int u=1;u<=n;u++)
                for(int j=0;j<G[u].size();j++)
                 {
                   int v=G[u][j];
                   if(sccno[u]==sccno[v]) continue;
                   degin[sccno[v]]++;
                   degout[sccno[u]]++;
                 }
    
            for(int i=1;i<=scc_cnt;i++)
                {
                    if(!degin[i]) in0++;
                    if(!degout[i]) out0++;
                }
    
            if(scc_cnt==1) printf("1
    0
    ");
            else printf("%d
    %d
    ",in0,max(in0,out0));
        }
        return 0;
    }
    

    分析:很好的一道题

    1:第一问,其实只要求出整个图中缩点后(强连通)入度数为0的点个数就好,因为缩成DAG后只要一个点

    有入度,那么我们肯定可以通过他的入度的那个点给他传信息;

    2.其实就是在问,给缩点之后的DAG添加多少条边可以使得DAG变成强连通,对于一个DAG只要他入度为0的点与出度为0的点均不存在,那么就可以缩成强连通,每添加一条边,可同时消灭一个出度为0的点与入度为0的点,所以取这两种点的最大值就好;

    3,最后特判一下原图本就是强连通的情况,因为这时degin[1]与degout[1]均会++

    证明:反证法

    如果一个DAG里面,每个点的出度都不为0,那么从任意一个点v1出发,都可以找到v2满足<v1,v2>属于G

    这样可以一直生成一个序列v1, v2, v3...因为图是有限的,这个序列一定会有环,所以这个图不是DAG

  • 相关阅读:
    url的非法字符有哪些
    asp.net各种获取客户端ip方法
    可编辑表格
    菜单弹出隐藏
    淡入淡出窗口
    使用XML传递数据
    Intellij IDEA将工程打包成jar包并执行
    使用JavaScript实现ajax
    AJAX基本演示使用
    统计指定目录下的视频时长
  • 原文地址:https://www.cnblogs.com/smilesundream/p/5475706.html
Copyright © 2020-2023  润新知