• 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));
    }
    }


     

  • 相关阅读:
    打印乘法口诀
    sum() 求和用法
    Python 2 和 Python 3 有哪些主要区别
    列表 enumerat 解包, 针对索引和元素
    冒泡排序,纯数字列表排序 解包,加中间值
    python 字符串与列表的相互转换 数据类型转换
    赋值 深浅拷贝
    python去掉字符串中空格的方法
    #上节多线程内容回顾#多线程的使用场景 #多进程的基本使用
    #queue队列 #生产者消费者模型
  • 原文地址:https://www.cnblogs.com/sook/p/2227144.html
Copyright © 2020-2023  润新知