Tarjan(强连通分量缩点) - Network of Schools - POJ 1236
一些学校连接在一个计算机网络上,学校之间存在软件支援协议,每个学校都有它应支援的学校名单(学校A支援学校B,并不表示学校B一定要支援学校A)。
当某校获得一个新软件时,无论是直接获得还是通过网络获得,该校都应立即将这个软件通过网络传送给它应支援的学校。
因此,一个新软件若想让所有学校都能使用,只需将其提供给一些学校即可。
现在请问最少需要将一个新软件直接提供给多少个学校,才能使软件能够通过网络被传送到所有学校?
最少需要添加几条新的支援关系,使得将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件?
输入格式
第1行包含整数N,表示学校数量。
第2…N+1行,每行包含一个或多个整数,第i+1行表示学校 i 应该支援的学校名单,每行最后都有一个0表示名单结束(只有一个0即表示该学校没有需要支援的学校)。
输出格式
输出两个问题的结果,每个结果占一行。
数据范围
2 ≤ N ≤ 100 2≤N≤100 2≤N≤100
输入样例:
5
2 4 3 0
4 5 0
0
0
1 0
输出样例:
1
2
分析:
分 析 题 意 : 分析题意: 分析题意:
第 一 问 : 至 少 从 几 个 点 出 发 , 能 够 遍 历 图 中 所 有 点 。 第一问:至少从几个点出发,能够遍历图中所有点。 第一问:至少从几个点出发,能够遍历图中所有点。
第 二 问 : 至 少 添 加 几 条 边 , 能 够 使 整 个 图 变 成 强 连 通 图 。 第二问:至少添加几条边,能够使整个图变成强连通图。 第二问:至少添加几条边,能够使整个图变成强连通图。
我 们 首 先 通 过 t a r j a n 算 法 进 行 缩 点 , 得 到 一 个 拓 扑 图 。 我们首先通过tarjan算法进行缩点,得到一个拓扑图。 我们首先通过tarjan算法进行缩点,得到一个拓扑图。
接 着 统 计 拓 扑 图 内 , 每 个 强 连 通 分 量 的 出 度 和 入 度 。 接着统计拓扑图内,每个强连通分量的出度和入度。 接着统计拓扑图内,每个强连通分量的出度和入度。
因 为 每 个 强 连 通 分 量 内 部 的 点 都 是 相 互 连 通 的 , 因为每个强连通分量内部的点都是相互连通的, 因为每个强连通分量内部的点都是相互连通的,
所 以 要 想 遍 历 图 中 所 有 点 , 我 们 至 少 要 从 拓 扑 图 中 的 所 有 起 点 出 发 。 所以要想遍历图中所有点,我们至少要从拓扑图中的所有起点出发。 所以要想遍历图中所有点,我们至少要从拓扑图中的所有起点出发。
拓 扑 图 中 起 点 的 数 量 即 强 连 通 分 量 入 度 为 零 的 点 的 数 量 P 。 拓扑图中起点的数量即强连通分量入度为零的点的数量P。 拓扑图中起点的数量即强连通分量入度为零的点的数量P。
因 此 第 一 问 的 答 案 为 P 。 因此第一问的答案为P。 因此第一问的答案为P。
对 于 第 二 问 , 假 设 出 度 为 0 的 强 连 通 分 量 数 量 为 Q , 结 论 : m a x ( P , Q ) 。 对于第二问,假设出度为0的强连通分量数量为Q,结论:max(P,Q)。 对于第二问,假设出度为0的强连通分量数量为Q,结论:max(P,Q)。
证明:
拓 扑 图 中 , 起 点 和 终 点 的 对 称 的 , 故 我 们 假 设 P ≤ Q 。 拓扑图中,起点和终点的对称的,故我们假设P≤Q。 拓扑图中,起点和终点的对称的,故我们假设P≤Q。
① 、 当 P = 1 时 , 在 所 有 终 点 到 起 点 之 间 均 连 接 一 条 有 向 边 , 即 可 形 成 强 连 通 图 , 共 需 要 增 加 Q 条 有 向 边 。 ①、当P=1时,在所有终点到起点之间均连接一条有向边,即可形成强连通图,共需要增加Q条有向边。 ①、当P=1时,在所有终点到起点之间均连接一条有向边,即可形成强连通图,共需要增加Q条有向边。
② 、 当 P > 1 时 , ②、当P>1时, ②、当P>1时,
任 选 两 条 起 点 和 终 点 不 同 的 路 线 : P 1 − > Q 1 和 P 2 − > Q 2 。 任选两条起点和终点不同的路线:P_1->Q_1和P_2->Q_2。 任选两条起点和终点不同的路线:P1−>Q1和P2−>Q2。
我 们 在 Q 2 与 P 1 之 间 连 接 一 条 有 向 边 , 则 这 两 条 路 线 即 可 视 作 一 条 路 线 : P 2 − > Q 1 。 我们在Q_2与P_1之间连接一条有向边,则这两条路线即可视作一条路线:P_2->Q_1。 我们在Q2与P1之间连接一条有向边,则这两条路线即可视作一条路线:P2−>Q1。
重 复 上 述 操 作 P − 1 次 , 起 点 仅 剩 下 1 个 , 终 点 剩 下 Q − ( P − 1 ) 个 。 重复上述操作P-1次,起点仅剩下1个,终点剩下Q-(P-1)个。 重复上述操作P−1次,起点仅剩下1个,终点剩下Q−(P−1)个。
P = 1 时 , 我 们 需 要 增 加 的 边 的 数 量 等 于 终 点 的 数 量 , 等 于 Q − ( P − 1 ) 个 。 P=1时,我们需要增加的边的数量等于终点的数量,等于Q-(P-1)个。 P=1时,我们需要增加的边的数量等于终点的数量,等于Q−(P−1)个。
故 总 共 我 们 需 要 新 增 P − 1 + Q − ( P − 1 ) = Q 条 边 。 故总共我们需要新增P-1+Q-(P-1)=Q条边。 故总共我们需要新增P−1+Q−(P−1)=Q条边。
故 第 二 问 的 结 论 为 m a x ( P , Q ) 。 故第二问的结论为max(P,Q)。 故第二问的结论为max(P,Q)。
注意:
对 于 第 二 问 , 若 强 连 通 分 量 仅 有 1 个 , 即 原 图 本 身 就 是 强 连 通 图 , 特 判 输 出 0 。 对于第二问,若强连通分量仅有1个,即原图本身就是强连通图,特判输出0。 对于第二问,若强连通分量仅有1个,即原图本身就是强连通图,特判输出0。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110, M=10010;
int n;
int e[M],ne[M],h[N],idx;
int stk[N],top;
bool in_stk[N];
int din[N],dout[N];
int id[N],ssc_cnt;
int dfn[N],low[N],timestamp;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++timestamp;
stk[++top]=u,in_stk[u]=true;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(!dfn[j])
{
tarjan(j);
low[u]=min(low[u],low[j]);
}
else if(in_stk[j]) low[u]=min(low[u],dfn[j]);
}
if(dfn[u]==low[u])
{
++ssc_cnt;
int y;
do
{
y=stk[top--];
in_stk[y]=false;
id[y]=ssc_cnt;
}while(y!=u);
}
}
int main()
{
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
int t;
while(cin>>t,t) add(i,t);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;i++)
for(int j=h[i];~j;j=ne[j])
{
int k=e[j];
int a=id[i],b=id[k];
if(a!=b) din[b]++,dout[a]++;
}
int P=0,Q=0;
for(int i=1;i<=ssc_cnt;i++) //缩点后,图中只有ssc_cnt个点
{
if(!din[i]) P++;
if(!dout[i]) Q++;
}
printf("%d\n",P);
if(ssc_cnt==1) puts("0");
else printf("%d\n",max(P,Q));
return 0;
}