• 【强连通分量缩点】poj 1236 Network of Schools


    poj.org/problem?id=1236

    【题意】

    • 给定一个有向图,求:
    • (1)至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
    • (2)至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点

    【思路】

    • (1)强连通分量缩点后形成一个有向无环图,只要选择入度为0的顶点,其他顶点都可以被到达
    • (2)等价于一个有向无环图加最少加多少条边能够变成一个强连通图,取出度为0的点的个数和入度为0的点的个数的max,因为出度为0的点要加一条出边,入度为0的点要加一条入边
    • (2)特判特殊情况:强连通分量只有一个,这时虽然入度为0和出度为0的点都是一个,但不需要加边

    【AC】

      1 //#include<bits/stdc++.h>
      2 #include<iostream>
      3 #include<cstdio>
      4 #include<string>
      5 #include<cstring>
      6 #include<algorithm>
      7 #include<cmath>
      8 using namespace std;
      9 typedef long long ll;
     10 int n;
     11 const int maxm=10002;
     12 const int maxn=1e2+2;
     13 struct edge
     14 {
     15     int to;
     16     int nxt;
     17 }e[maxm];
     18 int head[maxn],tot;
     19 int dfn[maxn],low[maxn],id;
     20 int S[maxn],top;
     21 int num,belong[maxn];
     22 bool vis[maxn];
     23 bool in[maxn];
     24 bool out[maxn];
     25 void init()
     26 {
     27     memset(head,-1,sizeof(head));
     28     tot=0;
     29     id=0;
     30     top=0;
     31     num=0;
     32     memset(dfn,0,sizeof(dfn));
     33     memset(low,0,sizeof(low));
     34     memset(S,0,sizeof(S));
     35     memset(vis,false,sizeof(vis));
     36     memset(belong,0,sizeof(belong));
     37     memset(in,false,sizeof(in));
     38     memset(out,false,sizeof(out));    
     39 }
     40 void addedge(int u,int v)
     41 {
     42     e[tot].to=v;
     43     e[tot].nxt=head[u];
     44     head[u]=tot++;
     45 }
     46 void tarjan(int u)
     47 {
     48     dfn[u]=low[u]=++id;
     49     S[++top]=u;
     50     vis[u]=true;
     51     for(int i=head[u];i!=-1;i=e[i].nxt)
     52     {
     53         int v=e[i].to;
     54         if(!dfn[v])
     55         {
     56             tarjan(v);
     57             low[u]=min(low[u],low[v]);
     58         }
     59         else if(vis[v]) low[u]=min(low[u],dfn[v]);
     60     }
     61     if(dfn[u]==low[u])
     62     {
     63         num++;
     64         while(1)
     65         {
     66             belong[S[top]]=num;
     67             vis[S[top]]=false;
     68             if(S[top--]==u) break;
     69         }
     70     }
     71     
     72 }
     73 
     74 int main()
     75 {
     76     while(~scanf("%d",&n))
     77     {
     78         init();
     79         for(int i=1;i<=n;i++)
     80         {
     81             int x;
     82             while(1)
     83             {
     84                 scanf("%d",&x);
     85                 if(x==0) break;
     86                 addedge(i,x);
     87             }
     88         }
     89         for(int i=1;i<=n;i++)
     90         {
     91             if(!dfn[i]) tarjan(i);
     92         }
     93         for(int u=1;u<=n;u++)
     94         {
     95             for(int i=head[u];i!=-1;i=e[i].nxt)
     96             {
     97                 int v=e[i].to;
     98                 if(belong[u]==belong[v]) continue;
     99                 in[belong[v]]=true;
    100                 out[belong[u]]=true;
    101             }
    102         }
    103         int ans1=0;
    104         int ans2=0;
    105         for(int i=1;i<=num;i++)
    106         {
    107             if(!in[i]) ans1++;
    108             if(!out[i]) ans2++; 
    109         }
    110         int ans=max(ans1,ans2);
    111         if(num==1) ans=0;
    112         printf("%d
    %d
    ",ans1,ans);
    113     }
    114     return 0;
    115 }
    View Code

    【大佬博客】

    www.cnblogs.com/kuangbin/archive/2011/08/07/2130277.html

    解题思路:

    —        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点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,

    这需要加n条边

    若 m <= n,则

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

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

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

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

  • 相关阅读:
    MATLAB画图之多个图以子图的形式合为一个图
    MATLAB画图之图中画局部放大的图中图
    MATLAB画图之自定义图片大小
    "廖雪峰的Git教程"学习笔记
    读书笔记之《程序员必读的职业规划书》
    Python初学者的资源总结
    时间管理去何方
    21天战拖记——Day21:《小强升职记》学习感受(2014-05-24)
    21天战拖记——Day20:整理自己的桌面(2014-05-23)
    21天战拖记——Day19:甘特图的使用(2014-05-22)
  • 原文地址:https://www.cnblogs.com/itcsl/p/7441796.html
Copyright © 2020-2023  润新知