PKU 2553 The Bottom of a Graph - 极大强连通分量

PKU2553 ZJU1979 The Bottom of a Graph

题目描述:

给出一个有向图,定义:若节点v所有能到达的点{wi},都能反过来到达v,那么称节点v是sink。题目要求所有的sink点。

分析:

很类似这样一个题,足球队队员之间某些人可以联系到另一些人,问教练至少要通知多少个队员,可以让所有队员都收到训练通知。

这个经典题目在吴文虎的图论书里面有讲解,就是求有向图的最小点基。方法如下:

首先将有向图划分成若干个极大强连通分量。那么对于每个强连通分量Ci来说,只需要通知一个人。而这些强连通分量之间要么相互独立,要么存在单向边从连通分量Ci指向Cj,构成森林。对于每棵树来说,只需要通知根结点Croot当中的任意一人,整棵树都可以被通知到。那么,只需要统计入度为0的连通分量的个数即可。

 

现在再回到这个题目,类似地,题目要求的实际上就是所有在出度为0的连通分量当中的节点。按序号输出即可。

此题的关键在求有向图的强连通分量。后面的工作都很简单。

另外,很高兴的是在此题ZJU刷到了第一:)~

Submit Time                  Language    Run Time(ms)       Run Memory(KB)         User Name
2008-11-05 21:53:04      C++           30                            628                               Tiaotiao

  1. /*
  2. PKU2553 The Bottom of a Graph
  3. */
  4. #include <stdio.h>
  5. #include <memory.h>
  6. #define clr(a) memset(a,0,sizeof(a))
  7. #define N 5005
  8. #define M 50000
  9. typedef struct StrNode{
  10.     int j;
  11.     struct StrNode* next;
  12. }Node;
  13. Node mem[M];
  14. int memp;
  15. void addEdge(Node* e[],int i,int j){
  16.     Node* p = &mem[memp++];
  17.     p->j=j;
  18.     p->next = e[i];
  19.     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]) return;
  25.     
  26.     mark[i]=1;
  27.     if(!g_DFS_First) f[i]=*nf;  //反向搜索,获取连通分量编号
  28.     for(p=e[i];p!=NULL;p=p->next){
  29.         j=p->j;
  30.         DFS_conn(e,j,mark,f,nf);
  31.     }
  32.     if(g_DFS_First) f[(*nf)++]=i;   //正向搜索,获取时间戳
  33. }
  34. /*
  35. 有向图极大强连通分量
  36. 参数:
  37.     邻接表e[],节点数n。返回极大强连通分支的个数ncon。 
  38.     返回con[i]表示节点i所属强连通分量的编号,0 ~ ncon-1。
  39. */
  40. int Connection(Node* e[],int n,int con[]){
  41.     int i,j,k,mark[N],ncon;
  42.     int time[N],ntime;  //time[i]表示时间戳为i的节点 
  43.     Node *p,*re[N]; //反向边
  44.     
  45.     //构造反向边邻接表
  46.     clr(re);
  47.     for(i=0;i<n;i++){
  48.         for(p=e[i];p!=NULL;p=p->next)
  49.             addEdge(re,p->j,i);
  50.     }
  51.     
  52.     //正向DFS,获得时间戳 
  53.     g_DFS_First = 1;
  54.     clr(mark); clr(time); ntime=0;
  55.     for(i=0;i<n;i++){
  56.         if(!mark[i])
  57.             DFS_conn(e,i,mark,time,&ntime);
  58.     }
  59.     
  60.     //反向DFS,获得强连通分量 
  61.     g_DFS_First = 0;
  62.     clr(mark); clr(con); ncon=0;
  63.     for(i=n-1;i>=0;i--){
  64.         if(!mark[time[i]]){
  65.             DFS_conn(re,time[i],mark,con,&ncon);
  66.             ncon++;
  67.         }
  68.     }
  69. }
  70. //vars
  71. Node* e[N];
  72. int con[N],ncon;
  73. int n,m;
  74. int d[N];
  75. int main()
  76. {
  77.     int i,j,k,x,y;
  78.     Node *p;
  79.     
  80.     while(scanf("%d",&n)!=EOF && n){
  81.         //init
  82.         memp=0; clr(e);
  83.         //input
  84.         scanf("%d",&m);
  85.         for(k=0;k<m;k++){
  86.             scanf("%d%d",&i,&j); i--; j--;
  87.             addEdge(e,i,j);
  88.         }
  89.         //Connection
  90.         ncon=Connection(e,n,con);
  91.         //work d[]
  92.         clr(d);
  93.         for(i=0;i<n;i++){
  94.             x=con[i];
  95.             for(p=e[i];p!=NULL;p=p->next){
  96.                 y=con[p->j];
  97.                 if(x!=y) d[x]++;
  98.             }
  99.         }
  100.         //output
  101.         j=0;
  102.         for(i=0;i<n;i++){
  103.             k=con[i];
  104.             if(d[k]==0){
  105.                 if(j) printf(" "); j=1;
  106.                 printf("%d",i+1);
  107.             }
  108.         }
  109.         puts("");
  110.     }
  111.     
  112.     return 0;
  113. }

你可能感兴趣的:(PKU 2553 The Bottom of a Graph - 极大强连通分量)