POJ 1236 NETWORK OF SCHOOLS

Link: http://poj.org/problem?id=1236

/******** HOW TO SOLVE *******
利用tarjan算出强连通图的集合,把一个强连通图看做一个节点,
算出每个节点之间的路径(即强连通图之间的路径)
设入度为0的点个数为fromNum,出度为0的点个数为outNum
答案即 fromNum 和 max(outNum, fromNum)
注意只有一个强连通图的特殊情况!!
******** HOW TO SOLVE ********/


/******* TO PROVE ********
<1>弱连通的有向图如果没有出度或入度为0的节点则它必强连通
=>弱连通的有向图所有节点的入度等于所有节点的出度。任给两个节点u,v,可以证明必有u到v的路。
<2>有向图入度,出度为0的节点数分别为Iz,Oz.则通过给它加边,使它变成强连通图,最少要加max{Iz,Oz}条边。
=>考虑将Oz个出度为0的节点与Iz个入度为0的节点相连接,若Oz!=Iz则将多出来的再接到其它节点上,以消除出度或入度为0的节点。即最少需加max{Iz, Oz}条边才能变成没有出度或入度为0的节点的有向图。
对于有多个弱连通分量的有向图,比如图G有Gx, Gy两个弱连通分量,其入度,出度为0的节点数分别为Ix,Ox和Iy,Oy. 不妨设Ox>=Iy,则考虑将Gx的Ox个出度为0的节点中的Iy个连到Gy的Iy个入度为0的节点上。这样就成了一个弱连通的有向图,它的入度,出度为0的节点数分别为Ix,Ox-Iy+Oy.则它的最少加边数为Iy+max{Ix,Ox-Iy+Oy},即为max{Ix+Iy,Ox+Oy}
posted by happynp
******* TO PROVE *********/


/***** NOTE ************
TLE 可能因为输入输出while循环无法跳出
反正一切while 或for 一直循环就会TLE
tarjan() 因为用多次 记得一开始vis instack都要赋值。。。
vis instack这种记录状态的数组 记得在状态改变时候 再次赋值!!!
代码里面有 邻接表的 也有邻接矩阵的实现 都AC 的
而且我看过了 时间和空间都没有区别 可能有会区别但是oj差别太小没显示出来吧。
邻接表比较适合稀疏图吧。。。
***** NOTE ***********/

View Code
#include <cstdio>
#include <iostream>
#include <stack>

using std::stack ;
const int maxn = 105;
int n, ConnCnt, index, rounds, dfn[maxn], low[maxn], conn[maxn][maxn];
int belong[maxn], out[maxn], from[maxn], map[maxn][maxn];
bool inStack[maxn], vis[maxn];
struct node
{
    int receive;
    node *next;
}school[maxn];
stack<int> s;
int min(int a, int b)
{
    return a <= b ?a :b ;
}
void init()
{
    ConnCnt = 0, index = 1;
    for(int i=1; i<=n; i++)
    {
        school[i].receive = i;
        school[i].next = NULL;
    }
}
void tarjan( int u )
{
    int i;
    dfn[u] = low[u] = index ++ ;
    s.push(u);
    inStack[u] = 1;
    vis[u] = 1;
    node *p = school[u].next;
    while( p!= NULL )
    {
        int v = p->receive ;
        if(!vis[v])
        {
            vis[v] = 1;
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(inStack[v])
            low[u] = min(low[u], dfn[v]);
        p = p->next;
    }
/*    for(int v=1; v<=n; v++)
    {
        if(map[u][v] && !vis[v])
        {
            vis[v] = 1;
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(map[u][v] && inStack[v])
            low[u] = min(low[u], dfn[v]);
        
    }
*/
    if(dfn[u] == low[u])
    {
        int size = 0;
        ConnCnt ++ ;
        while(i=s.top())
        {
//            conn[ConnCnt][++size] = i;
            belong[i] = ConnCnt;
            inStack[i] = 0;
            s.pop();
            if( i == u )
                break;
        }
    }

}
int check()
{
    for(int i=1; i<=n; i++)
        if(!vis[i])
            return i;
    return 0;
}
int main()
{
    int i, var;
    scanf("%d", &n);
    init();
    for(i=1; i<=n; i++)
    {
        while(scanf("%d", &var), var!=0 )
        {
            map[i][var] = 1;
            node *p = (node *)malloc(sizeof(node));
            p->receive = var;
            p->next = school[i].next;
            school[i].next = p; 
        }
    }
    while(i = check())
    {
        tarjan(i);
    }
    for(i=1; i<=n; i++)
    {
        node *p = school[i].next;
        while( p != NULL )
        {
            if(belong[p->receive] != belong[i])
            {
                out[belong[i]]++, from[belong[p->receive]]++;
            }
            p = p->next;
        }
    }
/*    for(i=1; i<=n; i++)
        for(int j=1; j<=n; j++)  //有向图不是i=j开始
        {
            if(map[i][j] && belong[i] != belong[j])
                out[belong[i]]++, from[belong[j]]++;
        }*/
    int outNum = 0, fromNum = 0;
    for(i=1; i<=ConnCnt; i++)
    {
        if(out[i] == 0)
            outNum ++;
        if(from[i] == 0)  //有可能入度为0 出度也为0 不用else if
            fromNum ++;
    }
    int max = outNum>=fromNum ?outNum :fromNum ;
//    int minNum = outNum+fromNum - max;
    if(ConnCnt == 1)
        printf("1\n");
    else
        printf("%d\n", fromNum);
    if(ConnCnt == 1)
        printf("0\n");
    else
        printf("%d\n", max);

    return 0;
}

 

转载于:https://www.cnblogs.com/crisxyj/archive/2013/02/27/2934869.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值