poj 1236:

题目大意:给出一个有向图, 任务一: 求最少的点,使得从这些点出发可以遍历整张图  任务二: 求最少加多少边 使整个图变成一个强连通分量。

首先任务一很好做, 只要缩点 之后 求 入度为0的点 的个数就好了。   因为 缩点后无环,任何一个 入度不为0的点, 沿着入边 倒着走回去一定可以到达一个入度为0的点。

任务二:

首先给出结论: 如果整个图已经是一个强连通分量,那么答案是0.  否则求出 缩点后入度为0的点和出度为0的点的个数a,b, 答案就是 max(a,b).

今天复习图论,第二次做这题了。 记得当时也不是很明白为什么这样是对的,网络上其他人的blog也都找不到证明。 这次觉得不能就这样放过,于是搜到原题的出处是IOI 1996, google到了 当年的官方题解:  http://wiki.ioinformatics.org/w/images/8/81/Ioi96net_sol.pdf

下面让我用自己的话翻译一下:

定义图G的一个dominator set(和传统的支配集定义好像不太一样?)为一个点集S1,图G的任意一个点都可以从S1集中的某个点出发到达。

定义图G的一个codominator set为一个点集S2,图G的任意一个点都可以到达S2集中的某个点。

设最小的dominator set是S1,最小的codominator set是S2。

根据定义很容易知道这里的S1就是任务一求出的那些点, S2就是把所有边反向之后任务一求出的点。

所以|S1| = 入度为0的点的个数  |S2| = 出度为0的点的个数

显然 $所需要的加的边数 >= max(|S1|, |S2|)$  因为一个强联通分量没有入度或者出度为0的点。

下面证明所需要的加的最少的边数是 $max(|S1|,|S2|)$ :

不妨假设$ |S1| <= |S2| $

1.如果$|S1|\ =\ 1\ $ 设 $S1\ =\ \{p\} \ \ \   S2\ =\ \{q_1, q_2, q_3... q_k\}$

那么连边$<q_1, p>,<q_2, p>,<q_3, p>...<q_k, p>$ 即可以让图变成一个强连通分量。

因为任意两点$u$, $v$,  存在路径$u\to q_i \to p \to v$。

2.如果$|S1|>1$  那么 存在 $p_1\ p_2 \in S1\ \ q_1\ q_2 \in S2$  根据S1,S2的定义 $p_1$到$q_1$有路, $p_2$到$q_2$有路。

那么先连边$<q_1, p_2>$构成一个新图G'。

可以证明 $S1'=S1-\{p_2\}$  $S2'=S2-\{q_1\}$ 分别是新图的最小dominator set和codominator set。

先证明S1'和S2'分别是新图的dominator set和codominator set。

因为原图G中任意$p_2$可以到达的点$v$, 在新图G'中可以从S1'中的点出发,由$p_1\to q_1 \to p_2 \to v$到达。

原图G中任意可以到达$q_1$的点$v$, 在新图G'中可以由$v\to q_1 \to p_2 \to q_2$到达S2'中的点。

在证明它们是最小的(官方题解中漏了这部分,我自己脑补的):

假设新图G’中存在比S1'还要小的dominator set S,  那么 $S + \{p_2\}$ 是原图G的dominator set,且比S1小, 这违反了S1是原图G最小的dominator set。

S2'的证明同理。

poj 1236代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <vector>
  6. #include <cmath>
  7. #include <map>
  8. using namespace std;
  9.  
  10. #define X first
  11. #define Y second
  12. #define N 20010
  13. #define M 350
  14.  
  15. typedef long long ll;
  16. const ll INF = 1ll<<;
  17. const int Mod = ;
  18.  
  19. int dfs_clock, scc_cnt;
  20. int dfn[N], low[N], scc[N];
  21. int in[N], out[N];
  22. int stack[N], top;
  23. vector<int> E[N];
  24.  
  25. void Dfs(int x)
  26. {
  27. dfn[x] = low[x] = ++dfs_clock; stack[++top] = x;
  28. for (int i = ; i < E[x].size(); ++i)
  29. {
  30. int y = E[x][i];
  31. if (!dfn[y]) Dfs(y), low[x] = min(low[x], low[y]);
  32. else if (!scc[y]) low[x] = min(low[x], dfn[y]);
  33. }
  34.  
  35. if (low[x] == dfn[x])
  36. {
  37. scc_cnt++;
  38. int cur;
  39. do
  40. {
  41. cur = stack[top--];
  42. scc[cur] = scc_cnt;
  43. }while (cur != x);
  44. }
  45. }
  46.  
  47. int main()
  48. {
  49. //freopen("A.in","r",stdin);
  50. //freopen("A.out","w",stdout);
  51.  
  52. int n;
  53. while (scanf("%d", &n) != EOF)
  54. {
  55. for (int i = ; i <= n; ++i) dfn[i] = low[i] = scc[i] = , E[i].clear();
  56. for (int i = , x; i <= n; ++i) while (scanf("%d", &x) && x) E[i].push_back(x);
  57. dfs_clock = scc_cnt = ;
  58. for (int i = ; i <= n; ++i) if (!dfn[i]) Dfs(i);
  59. for (int i = ; i <= scc_cnt; ++i) in[i] = out[i] = ;
  60. for (int i = ; i <= n; ++i)
  61. {
  62. for (int j = ; j < E[i].size(); ++j)
  63. {
  64. int x = scc[i], y = scc[E[i][j]];
  65. if (x != y) in[y]++, out[x]++;
  66. }
  67. }
  68. int a = , b = ;
  69. for (int i = ; i<= scc_cnt; ++i)
  70. {
  71. if (!in[i]) a++;
  72. if (!out[i]) b++;
  73. }
  74. printf("%d\n%d\n", a, scc_cnt > ? max(a,b) : );
  75. }
  76.  
  77. return ;
  78. }

双倍经验 hdu 2767代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <vector>
  6. #include <cmath>
  7. #include <map>
  8. using namespace std;
  9.  
  10. #define X first
  11. #define Y second
  12. #define N 20010
  13. #define M 350
  14.  
  15. typedef long long ll;
  16. const ll INF = 1ll<<;
  17. const int Mod = ;
  18.  
  19. int dfs_clock, scc_cnt;
  20. int dfn[N], low[N], scc[N];
  21. int in[N], out[N];
  22. int stack[N], top;
  23. vector<int> E[N];
  24.  
  25. void Dfs(int x)
  26. {
  27. dfn[x] = low[x] = ++dfs_clock; stack[++top] = x;
  28. for (int i = ; i < E[x].size(); ++i)
  29. {
  30. int y = E[x][i];
  31. if (!dfn[y]) Dfs(y), low[x] = min(low[x], low[y]);
  32. else if (!scc[y]) low[x] = min(low[x], dfn[y]);
  33. }
  34.  
  35. if (low[x] == dfn[x])
  36. {
  37. scc_cnt++;
  38. int cur;
  39. do
  40. {
  41. cur = stack[top--];
  42. scc[cur] = scc_cnt;
  43. }while (cur != x);
  44. }
  45. }
  46.  
  47. int main()
  48. {
  49. //freopen("A.in","r",stdin);
  50. //freopen("A.out","w",stdout);
  51.  
  52. int T, n, m; scanf("%d", &T);
  53. while (T-- && scanf("%d %d", &n, &m))
  54. {
  55. for (int i = ; i <= n; ++i) dfn[i] = low[i] = scc[i] = , E[i].clear();
  56. while (m--)
  57. {
  58. int x, y;
  59. scanf("%d %d", &x, &y);
  60. E[x].push_back(y);
  61. }
  62. dfs_clock = scc_cnt = ;
  63. for (int i = ; i <= n; ++i) if (!dfn[i]) Dfs(i);
  64. for (int i = ; i <= scc_cnt; ++i) in[i] = out[i] = ;
  65. for (int i = ; i <= n; ++i)
  66. {
  67. for (int j = ; j < E[i].size(); ++j)
  68. {
  69. int x = scc[i], y = scc[E[i][j]];
  70. if (x != y) in[y]++, out[x]++;
  71. }
  72. }
  73. int a = , b = ;
  74. for (int i = ; i<= scc_cnt; ++i)
  75. {
  76. if (!in[i]) a++;
  77. if (!out[i]) b++;
  78. }
  79. printf("%d\n", scc_cnt > ? max(a,b) : );
  80. }
  81.  
  82. return ;
  83. }

有向图 加最少的边 成为强连通分量的证明 poj 1236 hdu 2767的更多相关文章

  1. 有向图强连通分量的Tarjan算法和Kosaraju算法

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

  2. Tarjan算法 求 有向图的强连通分量

    百度百科 https://baike.baidu.com/item/tarjan%E7%AE%97%E6%B3%95/10687825?fr=aladdin 参考博文 http://blog.csdn ...

  3. tarjan 强连通分量

    一.强连通分量定义 有向图强连通分量在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly c ...

  4. HDU 3639 Hawk-and-Chicken(强连通分量+缩点)

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u013480600/article/details/32140501 HDU 3639 Hawk-a ...

  5. 图的强连通分量-Kosaraju算法

    输入一个有向图,计算每个节点所在强连通分量的编号,输出强连通分量的个数 #include<iostream> #include<cstring> #include<vec ...

  6. 强连通分量tarjan缩点——POJ2186 Popular Cows

    这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定 ...

  7. Kosaraju与Tarjan(图的强连通分量)

    Kosaraju 这个算法是用来求解图的强连通分量的,这个是图论的一些知识,前段时间没有学,这几天在补坑... 强连通分量: 有向图中,尽可能多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些 ...

  8. Tarjan算法求出强连通分量(包含若干个节点)

    [功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量.强连通分量是指有向图G里顶点间能互相到达的子图.而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连 ...

  9. 【数据结构】DFS求有向图的强连通分量

    用十字链表结构写的,根据数据结构书上的描述和自己的理解实现.但理解的不透彻,所以不知道有没有错误.但实验了几个都ok. #include <iostream> #include <v ...

随机推荐

  1. leetcode练习之No.7------ 翻转整数reverse_integer

    原文地址:http://www.niu12.com/article/48 git地址:git@github.com:ZQCard/leetcode.git 给定一个 32 位有符号整数,将整数中的数字 ...

  2. Go 测试单个方法/性能测试

    Go 测试单个方法 gotest.go package mytest import ( "errors" ) func Division(a, b float64) (float6 ...

  3. Jquery获取对象的几种方式介绍

    参考: 1.http://blog.csdn.net/zengyonglan/article/details/53995295 2.http://api.jquery.com/category/sel ...

  4. knowledgeroot 配置

    首先下载 KnowledgeRoot 的安装包,就是一个压缩文件,解压缩后放到 WebRoot 下面 在浏览器中打开网站,自动提示进行安装,安装的过程很简单,安装结束后即可以使用. 安装包创建的数据库 ...

  5. Mycat探索之旅(4)----Mycat的自增长主键和返回生成主键ID的实现

    说明:MyCAT自增长主键和返回生成主键ID的实现 1) mysql本身对非自增长主键,使用last_insert_id()是不会返回结果的,只会返回0:这里做一个简单的测试 创建测试表 ------ ...

  6. HDU 5379 Mahjong tree(dfs)

    题目链接:pid=5379">http://acm.hdu.edu.cn/showproblem.php? pid=5379 Problem Description Little su ...

  7. 工作流学习——Activiti流程变量五步曲

    一.前言 上一篇文章我们将流程实例的启动与查询,任务的办理查询都进行了介绍,我们这篇文章来介绍activiti中的流程变量. 二.正文 流程变量与我们寻常理解的变量是一样的,仅仅只是是用在了我们act ...

  8. DEB方式在UBUNTU安装ODOO 8.0

    odoo在ubuntu最简单最快速安装方式是deb方式,基本无需再去改数据库配置文件,全自动化了,odoo中文网推荐新手采用此方法 1 安装数据库:sudo apt-get install postg ...

  9. 为每一个应用程序池单独设置aspnet.config配置文件

    ASP.NET2.0之后的版本号就在各Framework的根文件夹下提供了一个aspnet.config文件.这个文件用来配置全局的一些信息,可是一直以来我们都没有怎么用过. ASP.NET4.0之后 ...

  10. Nginx-安装依赖及配置详解

    依赖 在安装Nginx之前, 需确保系统已经安装了gcc. openssl-devel. pcre-devel和zlib-devel软件库 配置 Nginx的配置文件nginx.conf位于其安装目录 ...