PKU 1236 Network of Schools - 最小点基

来源:互联网 发布:手机壁纸软件哪个好 编辑:程序博客网 时间:2024/06/08 00:48

题目大意:

N个高校之间有一些单向的网络链接(N<100),当发布一个软件时,学校i收到软件时,它可以将软件发送给所有它链接到的学校。现在要求发布一款软件,最少需要发给多少个学校,使得所有学校都可以收到软件(问题A)。最少需要添加多少条单向网络链接,可以使得将软件任意发给一个学校,使得所有学校都可以收到(问题B)。

分析:

我们先来讨论问题A。这个问题在吴文虎的OI图论书上有介绍过,叫做有向图的最小点基

首先,求出有向图的极大强连通分量,在同一个强连通分量里的学校任意一个收到软件,整个强连通分量里的学校都可以收到。将每个强连通分量缩成一个点,构成一个新的有向无环图。当强连通分量i收到软件,那么i可达的强连通分量都可以收到软件。

我们称入度为0的强连通分量为最高强连通分量。显然,每个最高强连通分量都必须单独发送一次软件,而其他强连通分量都可以通过最高强连通分量到达。所以,最高强连通分量的个数也就是问题A的解

至于问题B,我猜测是MAX(入度为0的点的个数,出度为0的点的个数)没想到居然对了,也没仔细证明。注意的是,当原图只有一个强连通分量是,问题B的答案是0。

 

  1. /*
  2. PKU1236 Network of Schools
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <memory.h>
  7. #define clr(a) memset(a,0,sizeof(a))
  8. #define MAX(a,b) ((a)>(b)?(a):(b))
  9. #define N 105
  10. #define M 10005
  11. typedef struct NodeStr{
  12.     int j;
  13.     struct NodeStr *next;
  14. }Node;
  15. Node mem[M*2];
  16. int memp;
  17. void addEdge(Node *e[],int i,int j){
  18.     Node *p = &mem[memp++];
  19.     p->next = e[i]; p->j = j; e[i] = p;
  20. }
  21. int g_DFS_First;
  22. void DFS_conn(Node* e[],int i,int mark[],int f[],int* nf){
  23.     int j; Node* p;
  24.     if(mark[i]) returnelse mark[i]=1;
  25.     if(!g_DFS_First) f[i]=*nf;  //反向搜索,获取连通分量编号
  26.     for(p=e[i];p!=NULL;p=p->next) DFS_conn(e,p->j,mark,f,nf);
  27.     if(g_DFS_First) f[(*nf)++]=i;   //正向搜索,获取时间戳
  28. }
  29. int Connection(Node* e[],int n,int con[]){
  30.     int i,j,k,mark[N],ncon,time[N],ntime;//time[i]表示时间戳为i的节点
  31.     Node *p,*re[N]; //反向边
  32.     clr(re);        //构造反向边邻接表
  33.     for(i=0;i<n;i++) for(p=e[i];p!=NULL;p=p->next) addEdge(re,p->j,i);
  34.     g_DFS_First = 1;    //正向DFS,获得时间戳 
  35.     clr(mark); clr(time); ntime=0;
  36.     for(i=0;i<n;i++) if(!mark[i]) DFS_conn(e,i,mark,time,&ntime);
  37.     
  38.     g_DFS_First = 0;    //反向DFS,获得强连通分量
  39.     clr(mark); clr(con); ncon=0;
  40.     for(i=n-1;i>=0;i--) if(!mark[time[i]])
  41.     { DFS_conn(re,time[i],mark,con,&ncon); ncon++; }
  42.     
  43.     return ncon;
  44. }
  45. /*
  46. 收缩强连通分量
  47. 参数:
  48.     e[]有向图邻接表.返回强连通分量个数m.
  49.     ce[]返回收缩强连通分量后的有向图邻接表[0,m-1],
  50.     con[]返回顶点i所属强连通分量的编号con[i]
  51. */
  52. int ShrinkConnection(Node *e[],int n,Node *ce[],int con[]){
  53.     int i,j,k,m; Node *p,*q;
  54.     m=Connection(e,n,con);
  55.     for(i=0;i<m;i++) ce[i]=NULL;
  56.     
  57.     for(k=0;k<n;k++){
  58.         for(i=con[k],p=e[k];p!=NULL;p=p->next){
  59.             for(j=con[p->j],q=ce[i];q!=NULL;q=q->next)
  60.                 if(q->j == j) break;
  61.             if(q==NULL&&i!=j) addEdge(ce,i,j);
  62.         }
  63.     }
  64.     return m;
  65. }
  66. /*****************************************/
  67. int main()
  68. {
  69.     int i,j,k;
  70.     int n,m,con[N],dout[N],din[N];
  71.     Node *e[N],*ce[N],*p;
  72.     int ansA,ansB;
  73.     
  74.     while(scanf("%d",&n)!=EOF){
  75.         //init
  76.         memp=0;
  77.         clr(e);
  78.         //input
  79.         for(i=0;i<n;i++){
  80.             while(scanf("%d",&j),j)
  81.                 addEdge(e,i,j-1);
  82.         }
  83.         //ShrinkConnection
  84.         m=ShrinkConnection(e,n,ce,con);
  85.         //work d[] - ce
  86.         clr(din); clr(dout);
  87.         for(i=0;i<m;i++){
  88.             for(p=ce[i];p!=NULL;p=p->next)
  89.                 din[p->j]++,dout[i]++;
  90.         }
  91.         
  92.         //get ans
  93.         ansA=ansB=0;
  94.         for(i=0;i<m;i++){
  95.             if(din[i]==0) ansA++;
  96.             if(dout[i]==0) ansB++;
  97.         }
  98.         ansB = MAX(ansA,ansB);
  99.         if(m==1) ansB=0;
  100.         
  101.         printf("%d/n%d/n",ansA,ansB);
  102.         
  103.     }
  104.     
  105.     return 0;
  106. }
原创粉丝点击