POJ 1236 Network of Schools 强联通分量 + 缩点
2012-08-20 20:43 javaspring 阅读(222) 评论(0) 编辑 收藏 举报来源:http://poj.org/problem?id=1236
题意:有一些学校,这些学校之间有一些边,边是单向边。现在有一套软件,如果一个学校有了这套软件,则该学校能到达的学校也就可以拥有这套软件。问至少需要几套软件,使得所有的学校都拥有软件,再问:至少需要添加几条边能够使得任意两个学校之间可达。
思路:其实就是一道强联通分量+缩点的模板题目。可以先算出强联通分量的数目,之后进行缩点。缩点后,统计出度为0的点的个数和入度为0的点的个数。则入度为0的点即为需要软件的数目,两者之间较大者即为需要添加边的数目。
说一下我对强联通分量的理解:
一个强联通分量指的是一个有向图内任意两点可达。我们可以用targin算法求强联通分量。targin算法中有两个数组,一个是low数组,一个是dfn数组。其中dfn数组表示的是访问这个点的时间,low数组比较难以理解。我是这样理解的,low数组就是该点所属的强联通分量的根节点的访问时间。说起来有点绕嘴,这个需要仔细理解。这样的话,当碰到树枝边时,举例来说,访问到了u,u到v有边,v此时还没有被访问,则边uv是树枝边。当边为树枝边时,low[u] = min(low[u],low[v]),当v被访问过且v仍旧在栈中的时候,边uv为后向边。此时low[u] = min (low[u], dfn[v])。其实这就是一个深搜的过程。如果某个点的low[x] = dfn[x],则x为一个强联通分量的根节点,栈中x以上的点都是以x为根的强联通分量中的点。
所谓缩点,就是把一个强联通分量作为一个点。
该题代码:
#include <iostream> #include <cstdio> #include <string.h> #include <vector> #include <queue> #include <stack> using namespace std; #define CLR(arr,val) memset(arr,val,sizeof(arr)) const int N = 110; int vis[N],head[N],low[N],dfn[N],numQ[N],indegree[N],outdegree[N]; int ss[N],cnts = 0;//stack int numnode,numedge,timevis,numcnt; struct edge{ int x,y,next; }ee[N*N]; void add_edge(int lp,int rp){ ee[numedge].x = lp; ee[numedge].y = rp; ee[numedge].next = head[lp]; head[lp] = numedge++; } int min(int x,int y){ return x < y ? x : y; } void targin(int x){ dfn[x] = low[x] = ++timevis; vis[x] = 1; cnts++; ss[cnts] = x; for(int i = head[x]; i != -1; i = ee[i].next){ int rp = ee[i].y; if(!vis[rp]){ targin(rp); low[x] = min(low[x],low[rp]); } else if(vis[rp] == 1){ low[x] = min(low[x],dfn[rp]); } } if(dfn[x] == low[x]){ //printf("x == %d\n",x); numcnt++; while(1){ int tt = ss[cnts--]; //printf("tt = %d\n",tt); //printf("numcnt = %d\n",numcnt); vis[tt] = 2; numQ[tt] = numcnt; if(tt == x)break; } } } int main(){ //freopen("1.txt","r",stdin); while(scanf("%d",&numnode) != EOF){ CLR(head,-1); CLR(dfn,0); CLR(low,0); numedge = 0; timevis = 0; numcnt = 0; cnts = 0; CLR(ss,0); for(int i = 1; i <= numnode;++i){ int x; while(scanf("%d",&x)&&x){ add_edge(i,x); } } for(int i = 1; i <= numnode; ++i){ if(!dfn[i]) targin(i); } //printf("numcnt = %d\n",numcnt); if(numcnt == 1){ printf("1\n0\n"); } else{ for(int i = 0; i < numedge; ++i){ int lp = ee[i].x; int rp = ee[i].y; if(numQ[lp] != numQ[rp]){ //printf("%d %d\n",numQ[lp],numQ[rp]); outdegree[numQ[lp]]++; indegree[numQ[rp]]++; } } int ans1 = 0,ans2 = 0; for(int i = 1; i <= numcnt; ++i){ if(indegree[i] == 0) ans1++; if(outdegree[i] == 0) ans2++; } printf("%d\n",ans1); printf("%d\n",max(ans1,ans2)); } } return 0; }