题目大意:
只有一个测例,一个有N个点的有向图(2 ≤ N ≤ 100),编号1 ~ N,接下来给出各个点连出去的有向边,具体形式如
5 //N
2 4 3 0 //1号点的3条有向边,1 -> 2, 1 -> 4, 1 -> 3,最后的0表示结束
4 5 0 //2号点
0 //3号
0 //4号
1 0 //5号
现在需要在此网络中传播信息,问至少需要给多少个点分发信息源,可以使得这些信息顺着有向边传递到整个网络,第二个问题是至少需要增加多少条有向边可以将信息源分发给任意一个点而使信息遍布整个网络(即让改图称为强连通的),两个答案占一行。
强连通分量(SCC:Strongly Connected Component)
Kosaraju:
注释代码:
/*
* Problem ID : POJ 1236 Network of Schools
* Author : Lirx.t.Una
* Language : C++
* Run Time : 16 ms
* Run Memory : 188 KB
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>
//学校的最大数量
#define MAXN 100
using namespace std;
bool g[MAXN + 1][MAXN + 1];//邻接矩阵表示有向图
bool vis[MAXN + 1];//深搜时标记点是否访问过
char idg[MAXN + 1];//indegree,缩点后每个点的入度
char odg[MAXN + 1];//outdegree,缩点后每个点的出度
char blg[MAXN + 1];//blong,表示某个点输入哪个强连通分量中
//由于经过强连通分量分解后形成一个邮箱无环图,每个点都属于某个SCC中
//有可能一个SCC中只有一个点
int n;//学校数量
int nb;//number of blongs,SCC的数量
stack<char> ord;//order,正向深搜时的访问顺序(后序遍历)
void
dfs(int u) {//正向深搜,后序遍历
int v;
vis[u] = true;
for ( v = 1; v <= n; v++ )
if ( !vis[v] && g[u][v] )
dfs(v);
ord.push(u);//先访问的最后入栈
}
void
_dfs(int u) {//反向深搜
int v;
vis[u] = true;
blg[u] = nb;//只要一次深搜能遍历的所有点都处于一个SCC中
for ( v = 1; v <= n; v++ )
if ( !vis[v] && g[v][u] )
_dfs(v);
}
void
kosa(void) {//Kosaraju SCC分解
int i;
for ( i = 1; i <= n; i++ )//全部正向深搜(有可能分解后的缩点图不连通)
if ( !vis[i] )
dfs(i);
//SCC中必含有环,因此反向深搜时必能回环,能回环的必为SCC
memset(vis, 0, sizeof(vis));
for ( nb = 0; !ord.empty(); ord.pop() )//再反向深搜
if ( !vis[ i = ord.top() ] )
++nb, _dfs(i);//注意SCC的编号递增
}
int
main() {
int u, v;
int idz, odz;//number of zero indegree and outdegree point
//入度为0的点以及出度为0的点的个数
int i;
scanf("%d", &n);
for ( u = 1; u <= n; u++ )
while ( scanf("%d", &v), v )
g[u][v] = true;
kosa();
//缩点图中idz即为软件分发数量(第一问答案)
//max( idz, odz )即为需要增加的有向边的数量呢(第二问答案)
if ( 1 == nb ) {//特殊情况,分解后缩成一个点
puts("1\n0");
return 0;
}
for ( u = 1; u <= n; u++ )//计算入度和出度
for ( v = 1; v <= n; v++ )
if ( g[u][v] && blg[u] != blg[v] ) {
idg[ blg[v] ]++;
odg[ blg[u] ]++;
}
idz = 0;
odz = 0;
for ( i = 1; i <= nb; i++ ) {//统计idz和odz
idz += !idg[i];
odz += !odg[i];
}
printf("%d\n%d\n", idz, ( idz > odz ? idz : odz ));
return 0;
}
无注释代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>
#define MAXN 100
using namespace std;
bool g[MAXN + 1][MAXN + 1];
bool vis[MAXN + 1];
char idg[MAXN + 1];
char odg[MAXN + 1];
char blg[MAXN + 1];
int n;
int nb;
stack<char> ord;
void
dfs(int u) {
int v;
vis[u] = true;
for ( v = 1; v <= n; v++ )
if ( !vis[v] && g[u][v] )
dfs(v);
ord.push(u);
}
void
_dfs(int u) {
int v;
vis[u] = true;
blg[u] = nb;
for ( v = 1; v <= n; v++ )
if ( !vis[v] && g[v][u] )
_dfs(v);
}
void
kosa(void) {
int i;
for ( i = 1; i <= n; i++ )
if ( !vis[i] )
dfs(i);
memset(vis, 0, sizeof(vis));
for ( nb = 0; !ord.empty(); ord.pop() )
if ( !vis[ i = ord.top() ] )
++nb, _dfs(i);
}
int
main() {
int u, v;
int idz, odz;
int i;
scanf("%d", &n);
for ( u = 1; u <= n; u++ )
while ( scanf("%d", &v), v )
g[u][v] = true;
kosa();
if ( 1 == nb ) {
puts("1\n0");
return 0;
}
for ( u = 1; u <= n; u++ )
for ( v = 1; v <= n; v++ )
if ( g[u][v] && blg[u] != blg[v] ) {
idg[ blg[v] ]++;
odg[ blg[u] ]++;
}
idz = 0;
odz = 0;
for ( i = 1; i <= nb; i++ ) {
idz += !idg[i];
odz += !odg[i];
}
printf("%d\n%d\n", idz, ( idz > odz ? idz : odz ));
return 0;
}
Tarjan:
注释代码:
/*
* Problem ID : POJ 1236 Network of Schools
* Author : Lirx.t.Una
* Language : C++
* Run Time : 32 ms
* Run Memory : 188 KB
*/
#include <iostream>
#include <cstdio>
#include <stack>
#define MAXN 100
using namespace std;
bool g[MAXN + 1][MAXN + 1];
char dfn[MAXN + 1];//dfs先序遍历时各点序号
char low[MAXN + 1];//low[i]表示i点下方的点(即通过i点可达并且dfn大于i的点)所能到达的dfn值最小的点的dfn值
char blg[MAXN + 1];
char idg[MAXN + 1];
char odg[MAXN + 1];
bool vis[MAXN + 1];//标记Tarjan算法中一个点是否在栈中
int n;
int nd, nb;//dfn序号和blg序号
stack<char> stk;
inline int
min( int a, int b ) {
return a < b ? a : b;
}
void
tar(int u) {//Tarjan SCC分解
int v;
stk.push(u);//先序遍历,没访问一个点就入栈
dfn[u] = low[u] = ++nd;//将dfn和low都初始为dfn值
vis[u] = true;//入栈标记
for ( v = 1; v <= n; v++ )
if ( g[u][v] )//找与之相连的点
if ( !dfn[v] ) {//如果没有访问过,dfn值从1开始,因此如果为0就代表没访问过
tar(v);//就先访问
low[u] = min( low[u], low[v] );//然后更新low[u]
}
else if ( vis[v] )//虽然访问过了,但是v在栈中,则dfn[v]必然比dfn[u]要小
low[u] = min( low[u], dfn[v] );//继续更新,low[u]有可能在for循环中之前的v里已经更新的很小了!
if ( dfn[u] == low[u] ) {//对u入口深搜结束后如果dfn和low还停留在初始值则必然代表u为一SCC的入口
//栈中从顶部到u都属于一个SCC
nb++;//给一个SCC的序号
do {
blg[ v = stk.top() ] = nb;
vis[v] = false;//弹栈标记
stk.pop();
} while ( !stk.empty() && v != u );//弹到u为止
}
}
int
main() {
int u, v;
int i, j;
int idz, odz;
scanf("%d", &n);
for ( u = 1; u <= n; u++ )
while ( scanf("%d", &v), v )
g[u][v] = true;
for ( u = 1; u <= n; u++ )//缩点图有可能有区域分离(不连通)
if ( !dfn[u] )
tar(u);
if ( 1 == nb ) {
puts("1\n0");
return 0;
}
for ( u = 1; u <= n; u++ )
for ( v = 1; v <= n; v++ )
if ( g[u][v] && ( i = blg[u] ) != ( j = blg[v] ) ) {
odg[i]++;
idg[j]++;
}
idz = 0;
odz = 0;
for ( i = 1; i <= nb; i++ ) {
idz += !idg[i];
odz += !odg[i];
}
printf("%d\n%d\n", idz, ( idz > odz ? idz : odz ));
return 0;
}
无注释代码:
#include <iostream>
#include <cstdio>
#include <stack>
#define MAXN 100
using namespace std;
bool g[MAXN + 1][MAXN + 1];
char dfn[MAXN + 1];
char low[MAXN + 1];
char blg[MAXN + 1];
char idg[MAXN + 1];
char odg[MAXN + 1];
bool vis[MAXN + 1];
int n;
int nd, nb;
stack<char> stk;
inline int
min( int a, int b ) {
return a < b ? a : b;
}
void
tar(int u) {
int v;
stk.push(u);
dfn[u] = low[u] = ++nd;
vis[u] = true;
for ( v = 1; v <= n; v++ )
if ( g[u][v] )
if ( !dfn[v] ) {
tar(v);
low[u] = min( low[u], low[v] );
}
else if ( vis[v] )
low[u] = min( low[u], dfn[v] );
if ( dfn[u] == low[u] ) {
nb++;
do {
blg[ v = stk.top() ] = nb;
vis[v] = false;
stk.pop();
} while ( !stk.empty() && v != u );
}
}
int
main() {
int u, v;
int i, j;
int idz, odz;
scanf("%d", &n);
for ( u = 1; u <= n; u++ )
while ( scanf("%d", &v), v )
g[u][v] = true;
for ( u = 1; u <= n; u++ )
if ( !dfn[u] )
tar(u);
if ( 1 == nb ) {
puts("1\n0");
return 0;
}
for ( u = 1; u <= n; u++ )
for ( v = 1; v <= n; v++ )
if ( g[u][v] && ( i = blg[u] ) != ( j = blg[v] ) ) {
odg[i]++;
idg[j]++;
}
idz = 0;
odz = 0;
for ( i = 1; i <= nb; i++ ) {
idz += !idg[i];
odz += !odg[i];
}
printf("%d\n%d\n", idz, ( idz > odz ? idz : odz ));
return 0;
}