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
- /*
- PKU2553 The Bottom of a Graph
- */
- #include <stdio.h>
- #include <memory.h>
- #define clr(a) memset(a,0,sizeof(a))
- #define N 5005
- #define M 50000
- typedef struct StrNode{
- int j;
- struct StrNode* next;
- }Node;
- Node mem[M];
- int memp;
- void addEdge(Node* e[],int i,int j){
- Node* p = &mem[memp++];
- p->j=j;
- p->next = e[i];
- e[i]=p;
- }
- int g_DFS_First;
- void DFS_conn(Node* e[],int i,int mark[],int f[],int* nf){
- int j; Node* p;
- if(mark[i]) return;
- mark[i]=1;
- if(!g_DFS_First) f[i]=*nf; //反向搜索,获取连通分量编号
- for(p=e[i];p!=NULL;p=p->next){
- j=p->j;
- DFS_conn(e,j,mark,f,nf);
- }
- if(g_DFS_First) f[(*nf)++]=i; //正向搜索,获取时间戳
- }
- /*
- 有向图极大强连通分量
- 参数:
- 邻接表e[],节点数n。返回极大强连通分支的个数ncon。
- 返回con[i]表示节点i所属强连通分量的编号,0 ~ ncon-1。
- */
- int Connection(Node* e[],int n,int con[]){
- int i,j,k,mark[N],ncon;
- int time[N],ntime; //time[i]表示时间戳为i的节点
- Node *p,*re[N]; //反向边
- //构造反向边邻接表
- clr(re);
- for(i=0;i<n;i++){
- for(p=e[i];p!=NULL;p=p->next)
- addEdge(re,p->j,i);
- }
- //正向DFS,获得时间戳
- g_DFS_First = 1;
- clr(mark); clr(time); ntime=0;
- for(i=0;i<n;i++){
- if(!mark[i])
- DFS_conn(e,i,mark,time,&ntime);
- }
- //反向DFS,获得强连通分量
- g_DFS_First = 0;
- clr(mark); clr(con); ncon=0;
- for(i=n-1;i>=0;i--){
- if(!mark[time[i]]){
- DFS_conn(re,time[i],mark,con,&ncon);
- ncon++;
- }
- }
- }
- //vars
- Node* e[N];
- int con[N],ncon;
- int n,m;
- int d[N];
- int main()
- {
- int i,j,k,x,y;
- Node *p;
- while(scanf("%d",&n)!=EOF && n){
- //init
- memp=0; clr(e);
- //input
- scanf("%d",&m);
- for(k=0;k<m;k++){
- scanf("%d%d",&i,&j); i--; j--;
- addEdge(e,i,j);
- }
- //Connection
- ncon=Connection(e,n,con);
- //work d[]
- clr(d);
- for(i=0;i<n;i++){
- x=con[i];
- for(p=e[i];p!=NULL;p=p->next){
- y=con[p->j];
- if(x!=y) d[x]++;
- }
- }
- //output
- j=0;
- for(i=0;i<n;i++){
- k=con[i];
- if(d[k]==0){
- if(j) printf(" "); j=1;
- printf("%d",i+1);
- }
- }
- puts("");
- }
- return 0;
- }