有向图 加最少的边 成为强连通分量的证明 poj 1236 hdu 2767
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代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <map>
using namespace std; #define X first
#define Y second
#define N 20010
#define M 350 typedef long long ll;
const ll INF = 1ll<<;
const int Mod = ; int dfs_clock, scc_cnt;
int dfn[N], low[N], scc[N];
int in[N], out[N];
int stack[N], top;
vector<int> E[N]; void Dfs(int x)
{
dfn[x] = low[x] = ++dfs_clock; stack[++top] = x;
for (int i = ; i < E[x].size(); ++i)
{
int y = E[x][i];
if (!dfn[y]) Dfs(y), low[x] = min(low[x], low[y]);
else if (!scc[y]) low[x] = min(low[x], dfn[y]);
} if (low[x] == dfn[x])
{
scc_cnt++;
int cur;
do
{
cur = stack[top--];
scc[cur] = scc_cnt;
}while (cur != x);
}
} int main()
{
//freopen("A.in","r",stdin);
//freopen("A.out","w",stdout); int n;
while (scanf("%d", &n) != EOF)
{
for (int i = ; i <= n; ++i) dfn[i] = low[i] = scc[i] = , E[i].clear();
for (int i = , x; i <= n; ++i) while (scanf("%d", &x) && x) E[i].push_back(x);
dfs_clock = scc_cnt = ;
for (int i = ; i <= n; ++i) if (!dfn[i]) Dfs(i);
for (int i = ; i <= scc_cnt; ++i) in[i] = out[i] = ;
for (int i = ; i <= n; ++i)
{
for (int j = ; j < E[i].size(); ++j)
{
int x = scc[i], y = scc[E[i][j]];
if (x != y) in[y]++, out[x]++;
}
}
int a = , b = ;
for (int i = ; i<= scc_cnt; ++i)
{
if (!in[i]) a++;
if (!out[i]) b++;
}
printf("%d\n%d\n", a, scc_cnt > ? max(a,b) : );
} return ;
}
双倍经验 hdu 2767代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <map>
using namespace std; #define X first
#define Y second
#define N 20010
#define M 350 typedef long long ll;
const ll INF = 1ll<<;
const int Mod = ; int dfs_clock, scc_cnt;
int dfn[N], low[N], scc[N];
int in[N], out[N];
int stack[N], top;
vector<int> E[N]; void Dfs(int x)
{
dfn[x] = low[x] = ++dfs_clock; stack[++top] = x;
for (int i = ; i < E[x].size(); ++i)
{
int y = E[x][i];
if (!dfn[y]) Dfs(y), low[x] = min(low[x], low[y]);
else if (!scc[y]) low[x] = min(low[x], dfn[y]);
} if (low[x] == dfn[x])
{
scc_cnt++;
int cur;
do
{
cur = stack[top--];
scc[cur] = scc_cnt;
}while (cur != x);
}
} int main()
{
//freopen("A.in","r",stdin);
//freopen("A.out","w",stdout); int T, n, m; scanf("%d", &T);
while (T-- && scanf("%d %d", &n, &m))
{
for (int i = ; i <= n; ++i) dfn[i] = low[i] = scc[i] = , E[i].clear();
while (m--)
{
int x, y;
scanf("%d %d", &x, &y);
E[x].push_back(y);
}
dfs_clock = scc_cnt = ;
for (int i = ; i <= n; ++i) if (!dfn[i]) Dfs(i);
for (int i = ; i <= scc_cnt; ++i) in[i] = out[i] = ;
for (int i = ; i <= n; ++i)
{
for (int j = ; j < E[i].size(); ++j)
{
int x = scc[i], y = scc[E[i][j]];
if (x != y) in[y]++, out[x]++;
}
}
int a = , b = ;
for (int i = ; i<= scc_cnt; ++i)
{
if (!in[i]) a++;
if (!out[i]) b++;
}
printf("%d\n", scc_cnt > ? max(a,b) : );
} return ;
}
有向图 加最少的边 成为强连通分量的证明 poj 1236 hdu 2767的更多相关文章
- 有向图强连通分量的Tarjan算法和Kosaraju算法
[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...
- Tarjan算法 求 有向图的强连通分量
百度百科 https://baike.baidu.com/item/tarjan%E7%AE%97%E6%B3%95/10687825?fr=aladdin 参考博文 http://blog.csdn ...
- tarjan 强连通分量
一.强连通分量定义 有向图强连通分量在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly c ...
- HDU 3639 Hawk-and-Chicken(强连通分量+缩点)
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u013480600/article/details/32140501 HDU 3639 Hawk-a ...
- 图的强连通分量-Kosaraju算法
输入一个有向图,计算每个节点所在强连通分量的编号,输出强连通分量的个数 #include<iostream> #include<cstring> #include<vec ...
- 强连通分量tarjan缩点——POJ2186 Popular Cows
这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定 ...
- Kosaraju与Tarjan(图的强连通分量)
Kosaraju 这个算法是用来求解图的强连通分量的,这个是图论的一些知识,前段时间没有学,这几天在补坑... 强连通分量: 有向图中,尽可能多的若干顶点组成的子图中,这些顶点都是相互可到达的,则这些 ...
- Tarjan算法求出强连通分量(包含若干个节点)
[功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量.强连通分量是指有向图G里顶点间能互相到达的子图.而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连 ...
- 【数据结构】DFS求有向图的强连通分量
用十字链表结构写的,根据数据结构书上的描述和自己的理解实现.但理解的不透彻,所以不知道有没有错误.但实验了几个都ok. #include <iostream> #include <v ...
随机推荐
- easyui datagrid checkbox的相关属性整理
DataGrid其中与选择,勾选相关 DataGrid属性: singleSelect boolean 如果为true,则只允许选择一行. false ctrlSelect boolean 在启用多行 ...
- dd-wrt端口映射不出去的解决办法
本人有一个巴法络的WZR-HP-G450H系统自带的固件不好用,但是随机却带了一个官方定制的DD-WRT,于是刷了去,但是今天在做一个FTP的时候突然无论怎么样映射或是做DMZ都不会出去,终于找到解决 ...
- ubuntu登录黑屏“failed to start session”, gdm+kdm+lightdm
sudo apt-get install ubuntu-desktop sudo systemctl start gdm sudo service lightdm restart sudo syste ...
- 快速打开IIS的方法
方法一: 在运行(win+r)输入inetmgr 方法二: 控制面板\所有控制面板项\管理工具\IIS 建议使用第一种方法
- photo sphere viewer使用图像数据替代路径来生成全景图
photo sphere viewer是一个js库,用来将全景图片生成360度的全景图像,但是其要求传入的是个路径.如何使用数据代替路径生成图像. 我采用的方法是用img标签生成图像,然后调用img. ...
- sublime 插件cssrem安装及配置
CSSREM CSSREM 是一个CSS的 px 值转 rem 值的Sublime Text3自动完成插件.先来看看插件的效果: 一个CSS的px值转rem值的Sublime Text 3自动完成插件 ...
- Linux下免安装mysql
我是使用免安装的包mysql-5.6.30-linux2.6-x86_64.tar.gz(在http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6 ...
- RocketMQ的异步调用
这个异步调用方法中传入一个final 回调对象. public void invokeAsyncImpl(final Channel channel, final RemotingCommand re ...
- Linux下安卓ndk混合编译调用so方法——QuickStart学习
转自:http://www.52pojie.cn/thread-313869-1-1.html #注意:.h 和.c中的错误eclipse不会检查,只会调用时在手机或虚拟机中死掉.因此需要仔细检查其中 ...
- mac下配置adb
博主近期搞了台macbook用,搞android开发爽多了.程序编译那个速度确实让我感到非常爽.尤其是在之前用windows时动辄启动eclipse几分钟,编译又花非常久的情况下,可是用了mac发现a ...