POJ 1236 Network of Schools

题目大意:

       只有一个测例,一个有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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值