传送门

题意:

  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=;//最大的节点个数,一般是 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=;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=;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=;i <= maxV;++i)
  47. if(!vis[i])
  48. Dfs(i);
  49. mem(vis,false);
  50. int sccId=;//DAG节点个数
  51. for(int i=vs.size()-;i >= ;--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=;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,);
  76. mem(out,);
  77.  
  78. for(int i=;i <= N;++i)
  79. for(int j=;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=;
  86. int zeroOut=;
  87. for(int i=;i <= sccId;++i)
  88. {
  89. zeroIn += (in[i] == ? :);
  90. zeroOut += (out[i] == ? :);
  91. }
  92. printf("%d\n",zeroIn);
  93. if(sccId == )
  94. printf("0\n");
  95. else
  96. printf("%d\n",max(zeroIn,zeroOut));
  97. }

分割线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=+;
  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<<];
  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&))
  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&))
  50. continue;
  51. RDFS(v,k);
  52. }
  53. }
  54. int scc()
  55. {
  56. mem(vis,false);
  57. vs.clear();
  58. for(int i=;i <= n;++i)
  59. if(!vis[i])
  60. DFS(i);
  61.  
  62. int k=;
  63. mem(vis,false);
  64. for(int i=vs.size()-;i >= ;--i)
  65. if(!vis[vs[i]])
  66. RDFS(vs[i],++k);
  67.  
  68. mem(in,);
  69. mem(out,);
  70. for(int u=;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&)
  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=;
  92. int zeroOut=;
  93. for(int i=;i <= k;++i)
  94. {
  95. if(!_scc.in[i])
  96. zeroIn++;
  97. if(!_scc.out[i])
  98. zeroOut++;
  99. }
  100. printf("%d\n%d\n",zeroIn,k == ? :max(zeroIn,zeroOut));
  101. }
  102. void Init()
  103. {
  104. num=;
  105. mem(head,-);
  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=;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 ;
  125. }

poj 1236(强连通分量分解模板题)的更多相关文章

  1. POJ(2186)强连通分量分解

    #include<cstdio> #include<vector> #include<cstring> using namespace std; ; vector& ...

  2. poj 2186 强连通分量

    poj 2186 强连通分量 传送门 Popular Cows Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 33414 Acc ...

  3. 算法数据结构 | 三个步骤完成强连通分量分解的Kosaraju算法

    强连通分量分解的Kosaraju算法 今天是算法数据结构专题的第35篇文章,我们来聊聊图论当中的强连通分量分解的Tarjan算法. Kosaraju算法一看这个名字很奇怪就可以猜到它也是一个根据人名起 ...

  4. 强连通分量分解 Kosaraju算法 (poj 2186 Popular Cows)

    poj 2186 Popular Cows 题意: 有N头牛, 给出M对关系, 如(1,2)代表1欢迎2, 关系是单向的且能够传递, 即1欢迎2不代表2欢迎1, 可是假设2也欢迎3那么1也欢迎3. 求 ...

  5. poj 1904(强连通分量+输入输出外挂)

    题目链接:http://poj.org/problem?id=1904 题意:有n个王子,每个王子都有k个喜欢的妹子,每个王子只能和喜欢的妹子结婚,大臣给出一个匹配表,每个王子都和一个妹子结婚,但是国 ...

  6. poj 2762(强连通分量+拓扑排序)

    题目链接:http://poj.org/problem?id=2762 题意:给出一个有向图,判断任意的两个顶点(u,v)能否从u到达v,或v到达u,即单连通,输出Yes或No. 分析:对于同一个强连 ...

  7. poj 1904(强连通分量+完美匹配)

    传送门:Problem 1904 https://www.cnblogs.com/violet-acmer/p/9739990.html 参考资料: [1]:http://www.cnblogs.co ...

  8. 强连通分量(Tarjan)模板

    贴模板,备忘. 模板1: #include<iostream> #include<cstring> #include<cmath> #include<cstd ...

  9. POJ - 2186  Popular Cows tarjain模板题

    http://poj.org/problem?id=2186 首先求出所有的强连通分量,分好块.然后对于每一个强连通分量,都标记下他们的出度.那么只有出度是0 的块才有可能是答案,为什么呢?因为既然你 ...

随机推荐

  1. poj1426 Find The Multiple(c语言巧解)

    Find The Multiple Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 36335   Accepted: 151 ...

  2. 12.13 Daily Scrum

    现在已经可以实现在应用中直接通过WebView浏览餐厅的网页,而不用再调用手机的浏览器. 收藏夹的功能也基本实现,接下来的目标时将收藏夹与每一个用户关联.   Today's Task Tomorro ...

  3. 【MOOC EXP】Linux内核分析实验七报告

    程涵  原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 可执行程序的装载 知识点梳理 一.预处 ...

  4. SVN解决冲突

    SVN冲突出现场景 如今是一个团结协作的时代,开发一个系统,往往会多人协作共同完成.版本管理是必不可少的,常用的软件有Git,SVN等.今天说一下,SVN管理版本时,如果出现冲突后,如何快速解决冲突. ...

  5. 剑值offer:最小的k个数

    这是在面试常遇到的topk问题. 题目描述: 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 解题思路: 思路一:用快排对数 ...

  6. SpringMVC 网站

    https://mp.weixin.qq.com/s/MLCIcQcevLBpZFOQce3DMQ

  7. HP 4411s Install Red Hat Enterprise Linux 5.8) Wireless Driver

    pick up from http://blog.163.com/wangkangming2008%40126/blog/static/78277928201131994053617/ # cp iw ...

  8. CentOS yum 安装获取原始rpm文件的方法

    1. 有时候 yum install 需要从几个repo下载rpm包速度很慢,不如自己能够将rpm包下载下来继续使用,比较好. 发现yum install 有两种方式能够将下载的rpm包保存下来. 方 ...

  9. 转帖: 一份超全超详细的 ADB 用法大全

    增加一句 连接 网易mumu模拟器的方法 adb  connect 127.0.0.1:7555 一份超全超详细的 ADB 用法大全 2016年08月28日 10:49:41 阅读数:35890 原文 ...

  10. datetime的小坑

    在做悦运动这个项目时,在创建约运动关系表时,运动开始时间,注意(导入datetime包时,我导入的是class datetime(__datetime.date)这个类,可以调用这个类中的方法,调用当 ...