zoukankan      html  css  js  c++  java
  • poj1236Network of Schools

    /*poj1236Network of Schools
    题意:网络中有一些学校,每个学校可以分发软件给其他学校。可以向哪个分发取决于他们各自维护的一个清单。有两个问题,:
    1:至少要copy多少份新软件给那些学校,才能使得每个学校都能得到。
    2:要在所有的学校的清单里面至少一共增加几项才能使得 把软件给任意一个学校,所有的学校都能收得到。
    思路:问题一:要使每个学校都能收到,就是计算图里面一共有多少入度是的点,这个好理解。
          问题二:先学习下计算强连通分支的算法——Tarjan,  然后缩点,把每一块看成一个点,出来一个新的有向图。然后计算出度为的入度为的点各有多少,大的那个就是答案。值得注意的是,当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0;
    */

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define MAXN 111
    int head[MAXN*MAXN],next[MAXN*MAXN],u[MAXN*MAXN],v[MAXN*MAXN];
    int DFN[MAXN],LOW[MAXN],instack[MAXN],Stap[MAXN],ID[MAXN],In[MAXN],Out[MAXN];
    int Dindex,Stop,Bcnt;
    void tarjan(int i)
    {
    int j;
    DFN[i]=LOW[i]=++Dindex;//定义DFN(u)为节点u搜索的次序编号,LOW(u)为u或u的子树能够追溯到的最早的栈中节点的次序
    instack[i]=true;//i在栈中
    Stap[++Stop]=i;//模拟栈
    for(int e=head[i];e>=0;e=next[e])
    {
    j=v[e];
    if(!DFN[j])//若这条边的终点没访问过则取查询该点
    {
    tarjan(j);
    if(LOW[j]<LOW[i])LOW[i]=LOW[j];//子树能追溯到的时间戳小则更新父亲追溯到的时间戳
    }
    else if(instack[j]&&DFN[j]<LOW[i])
    LOW[i]=DFN[j];
    }
    if(DFN[i]==LOW[i])//找到一个连通分量把这个连通分量退栈
    {
    Bcnt++;
    do
    {
    j=Stap[Stop--];
    instack[j]=false;
    ID[j]=Bcnt;//缩点后强连通区域个数
    }while(j!=i);
    }
    }
    int n,m,num;
    void pro()
    {
    Dindex=Stop=Bcnt=0;
    memset(DFN,0,sizeof(DFN));
    memset(ID,-1,sizeof(ID));
    for(int i=1;i<=n;i++)
    {
    if(!DFN[i])tarjan(i);
    }
    }
    int main()
    {
    while(scanf("%d",&n)==1)
    {
    num=0;
    for(int i=1;i<=n;i++)head[i]=-1,next[i]=-1;
    for(int i=1;i<=n;i++)
    {
    while(scanf("%d",&m))
    {
    if(!m)break;
    u[num]=i,v[num]=m;
    next[num]=head[i];
    head[i]=num;
    num++;
    }
    }
    pro();
    int in0=0,out0=0,Num=0;
    memset(In,0,sizeof(In));
    memset(Out,0,sizeof(Out));
    for(int i=0;i<num;i++)
    {
    int s=ID[u[i]];
    int t=ID[v[i]];
    if(s!=t)
    {
    In[t]=1;
    Out[s]=1;
    }
    }
    for(int i=1;i<=Bcnt;i++)
    {
    if(!In[i])in0++;//计算入度为的点的个数
    if(!Out[i])out0++;//计算出度为的点的个数
    }
    if(Bcnt==1)printf("1\n0\n");//连通分量为特殊情况
    else printf("%d\n%d\n",in0,max(in0,out0));
    }
    }


     

  • 相关阅读:
    FZU 2150 Fire Game
    POJ 3414 Pots
    POJ 3087 Shuffle'm Up
    POJ 3126 Prime Path
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    字符数组
    HDU 1238 Substing
    欧几里德和扩展欧几里德详解 以及例题CodeForces 7C
    Codeforces 591B Rebranding
  • 原文地址:https://www.cnblogs.com/sook/p/2227144.html
Copyright © 2011-2022 走看看