• poj 1236(强连通分量分解模板题)


    传送门

    题意:

      N(2<N<100)个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。

      问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。

      问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

    从题意中抽象出的算法模型:

      给定一个有向图,求:

      1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点。

      2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点。

    题解:

      1. 求出所有强连通分量

      2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。

      3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少。

      在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少。

      加边的方法:

      要为每个入度为0的点添加入边,为每个出度为0的点添加出边。

      假定有 n 个入度为0的点,m个出度为0的点,如何加边?

      把所有入度为0的点编号 0,1,2,3,4 ....N -1

      每次为一个编号为 i 的入度为0的点到出度为0的点,添加一条出边,这需要加n条边。

      若 m <= n,则

      加了这n条边后,已经没有入度0点,则问题解决,一共加了 n 条边

      若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。

      所以,max(m,n)就是第二个问题的解

      此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加边了,所以答案是0。

    以上解析来源于bin巨%%%%%%

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 #define pb push_back
     7 #define mem(a,b) memset(a,b,sizeof a)
     8 const int maxn=110;//最大的节点个数,一般是 1e5 级别的
     9 
    10 int scc[maxn];//所属强连通分量的拓扑排序
    11 bool vis[maxn];//vis[u] : dfs中判断节点u是否被访问过
    12 vector<int >vs;//后序遍历顺序的顶点列表
    13 vector<int >edge[maxn],redge[maxn];//边、反边
    14 
    15 void addEdge(int u,int v)
    16 {
    17     edge[u].pb(v);
    18     redge[v].pb(u);
    19 }
    20 void Dfs(int u)//第一次dfs,后序遍历标记,越靠近叶子结点标号越小
    21 {
    22     vis[u]=true;
    23     for(int i=0;i < edge[u].size();++i)
    24     {
    25         int to=edge[u][i];
    26         if(!vis[to])
    27             Dfs(to);
    28     }
    29     vs.pb(u);
    30 }
    31 void rDfs(int u,int sccId)//反向dfs,利用反向图,求出强连通分量个数
    32 {
    33     vis[u]=true;
    34     scc[u]=sccId;
    35     for(int i=0;i < redge[u].size();++i)
    36     {
    37         int to=redge[u][i];
    38         if(!vis[to])
    39             rDfs(to,sccId);
    40     }
    41 }
    42 int Scc(int maxV)
    43 {
    44     mem(vis,false);
    45     vs.clear();
    46     for(int i=1;i <= maxV;++i)
    47         if(!vis[i])
    48             Dfs(i);
    49     mem(vis,false);
    50     int sccId=0;//DAG节点个数
    51     for(int i=vs.size()-1;i >= 0;--i)
    52     {
    53         int to=vs[i];
    54         if(!vis[to])
    55         {
    56             sccId++;
    57             rDfs(to,sccId);
    58         }
    59     }
    60     return sccId;//返回强连通分量的个数
    61 }
    62 int main()
    63 {
    64     int N;
    65     scanf("%d",&N);
    66     for(int i=1;i <= N;++i)
    67     {
    68         int v;
    69         while(scanf("%d",&v) && v)
    70             addEdge(i,v);
    71     }
    72     int sccId=Scc(N);
    73     int in[maxn];
    74     int out[maxn];
    75     mem(in,0);
    76     mem(out,0);
    77 
    78     for(int i=1;i <= N;++i)
    79         for(int j=0;j < edge[i].size();++j)
    80         {
    81             int to=edge[i][j];
    82             if(scc[i] != scc[to])
    83                 out[scc[i]]++,in[scc[to]]++;
    84         }
    85     int zeroIn=0;
    86     int zeroOut=0;
    87     for(int i=1;i <= sccId;++i)
    88     {
    89         zeroIn += (in[i] == 0 ? 1:0);
    90         zeroOut += (out[i] == 0 ? 1:0);
    91     }
    92     printf("%d
    ",zeroIn);
    93     if(sccId == 1)
    94         printf("0
    ");
    95     else
    96         printf("%d
    ",max(zeroIn,zeroOut));
    97 }
    View Code

    分割线2019.4.27

    半年后的我,代码风格:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<vector>
      5 using namespace std;
      6 #define pb(x) push_back(x)
      7 #define mem(a,b) memset(a,b,sizeof(a))
      8 const int maxn=100+50;
      9 
     10 int n;
     11 int num;
     12 int head[maxn];
     13 struct Edge
     14 {
     15     int to;
     16     int next;
     17 }G[maxn*maxn<<1];
     18 void addEdge(int u,int v)
     19 {
     20     G[num]={v,head[u]};
     21     head[u]=num++;
     22 }
     23 struct SCC
     24 {
     25     int col[maxn];
     26     int in[maxn];
     27     int out[maxn];
     28     bool vis[maxn];
     29     vector<int >vs;
     30     void DFS(int u)
     31     {
     32         vis[u]=true;
     33         for(int i=head[u];~i;i=G[i].next)
     34         {
     35             int v=G[i].to;
     36             if(vis[v] || (i&1))
     37                 continue;
     38             DFS(v);
     39         }
     40         vs.pb(u);
     41     }
     42     void RDFS(int u,int k)
     43     {
     44         col[u]=k;
     45         vis[u]=true;
     46         for(int i=head[u];~i;i=G[i].next)
     47         {
     48             int v=G[i].to;
     49             if(vis[v] || !(i&1))
     50                 continue;
     51             RDFS(v,k);
     52         }
     53     }
     54     int scc()
     55     {
     56         mem(vis,false);
     57         vs.clear();
     58         for(int i=1;i <= n;++i)
     59             if(!vis[i])
     60                 DFS(i);
     61 
     62         int k=0;
     63         mem(vis,false);
     64         for(int i=vs.size()-1;i >= 0;--i)
     65             if(!vis[vs[i]])
     66                 RDFS(vs[i],++k);
     67 
     68         mem(in,0);
     69         mem(out,0);
     70         for(int u=1;u <= n;++u)
     71         {
     72             for(int i=head[u];~i;i=G[i].next)
     73             {
     74                 int v=G[i].to;
     75                 if(i&1)
     76                     continue;
     77                 if(col[u] != col[v])
     78                 {
     79                     out[col[u]]++;///col[u]:出度++
     80                     in[col[v]]++;///col[v]:入度++
     81                 }
     82             }
     83         }
     84         return k;
     85     }
     86 }_scc;
     87 void Solve()
     88 {
     89     int k=_scc.scc();
     90 
     91     int zeroIn=0;
     92     int zeroOut=0;
     93     for(int i=1;i <= k;++i)
     94     {
     95         if(!_scc.in[i])
     96             zeroIn++;
     97         if(!_scc.out[i])
     98             zeroOut++;
     99     }
    100     printf("%d
    %d
    ",zeroIn,k == 1 ? 0:max(zeroIn,zeroOut));
    101 }
    102 void Init()
    103 {
    104     num=0;
    105     mem(head,-1);
    106 }
    107 int main()
    108 {
    109 //    freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin);
    110     while(~scanf("%d",&n))
    111     {
    112         Init();
    113         for(int i=1;i <= n;++i)
    114         {
    115             int v;
    116             while(scanf("%d",&v) && v)
    117             {
    118                 addEdge(i,v);
    119                 addEdge(v,i);
    120             }
    121         }
    122         Solve();
    123     }
    124     return 0;
    125 }
    View Code
  • 相关阅读:
    泛型
    事件和委托
    参数和属性
    常量、字段、和方法
    LINQ
    LINQ- 子查询、组合策略、投影策略
    MySQL全局锁和表锁
    CLR基础
    LINQ
    LINQ
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/9740711.html
Copyright © 2020-2023  润新知