poj 1236 Network of Schools 强联通分量Kosaraju

Kosaraju算法

首先提出图的转置的概念。所谓转置就是将一个图上所有的有向边反向。简单来说就是本是x->y的一条边,现在变为y->x这样一条边。

另外强连通性质具有传递性,如果(i,j),(j,k)属于同一强连通分量,那么(i,k)属于同一强连通分量。因为如果满足题设,那么存在路径i->j->k和k->j->i。所以传递性得证。所以其实我们要求点i所属的极大强连通分量,只需要把所有和i可以互达的点求出来就可以了。

该算法的流程如下:

1、利用深度优先搜索遍历原图,并按出栈顺序的先后顺序把点放进一个线性表里。这里可以每次取任意一个点出发DFS,如果还有点未被DFS到,那么继续任选未被遍历到的点出发DFS。

2、将整个图转置。按照线性表中的出栈顺序,从后往前作为起点DFS。每次DFS到的就是一个强连通分量。

初看这个算法很神奇,但是其实是不难证明的。

命题1:该算法求出的都是强连通分量

假设在后来转置后的图中从x dfs到了 y处,说明存在路径x->y。因为这是在转置图中,所以说明原图中存在路径y->x

然后另外一个信息就是x的序号在y之后。这有两种可能:

1、以y为根先DFS出了一棵搜索树(可以认为是整个搜索树的一棵子树),但是这棵子树里不包含x,并且此时x还未被dfs到。(利用反证法,如果这棵子树里包含了x,那么x的序号会在y之前)

2、y是x扩展出来的搜索树中的一个结点。

综合两个条件,综合两个条件取交。那么上面两种可能中的第一种不成立。因为存在路径y->x,所以如果x未被dfs到,一定会被y为根的搜索树包含的。于是只剩下第二种可能,那么第二种情况表明存在路径x->y。所以x,y可以互相到达。至此证明了该算法求出的都是强连通分量。命题1得证。

命题2:所有的极大强连通分量都会被该算法求到。

命题2等价于:存在两个点(i,j),他们互相可达,但是没有被放进同一个强连通分量中。易知若(i,j)可以互相到达,那么肯定其中一个点在另外一个点扩展出去的搜索树中(注意DFS的性质,走完一个分叉再走另外一个分叉)。

由于轮换对称,不妨设j在i扩展出去的搜索树中,那么显然j比i先出栈。假如命题2不成立,那么必须是有一棵以A为根(而且这个A必须比i后出栈)的搜索树,它包含了j但不包含i。由命题1可知,(A,j)可互达。那么由于(i,j)可互达,在这颗搜索树中,肯定能有j扩展到i,所以这样的一棵搜索树是不存在的。因此反证得命题2成立。

该算法比Tarjan算法慢一点,但是有一个好处:该算法依次求出的强连通分量已经是拓扑序的。

下面给出这一性质的证明:

对于两个不同的强连通分量A,B,设A中出栈顺序最晚的点为a,B中出栈顺序最晚的点为b。不妨设a出栈顺序在b之前,那么有两种可能。

存在路径b->a。由于两点不属于同一强连通分量,所以不存在路径a->b。这种情况下Kosaraju算法会先把B强连通分量拿出来,所以是满足拓扑序的。

不存在路径b->a。那么这种情况下必然也不存在路径a->b,否则a出栈之时,b必然已经出栈了。所以,先拿出B强连通分量是符合拓扑序的。


题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。


解法:

     1:先跑一边tarjian算法。那么我们可以得出每一个学校在哪一个强连通分量里。然后算出入度为0 的强连通分量的个数。就是第一个答案。

     读为0的强连通分量的那些强连通分量连起来。所以算出出度为0和入度为0的最大值,就是第二个的答案。

      3:注意如果强连通分量的个数是一个,那么第二个的答案是0.


代码:

var
  tot,i,j,n,x,s1,s2,e,f1,e1:longint;
  v:array[1..100] of boolean;
  last,next,belong,rd,cd,f:array[1..100] of longint;
  side:array[1..20000] of record
    x,y,next:longint
  end;

procedure add(x,y:longint);
begin
  inc(e);
  side[e].x:=x;
  side[e].y:=y;
  side[e].next:=last[x];
  last[x]:=e;
end;

procedure dfs(x:longint);
var
  i:longint;
begin
  v[x]:=false;
  i:=last[x];
  while i>0 do
    with side[i] do
    begin
      if v[y] then dfs(y);
      i:=next;
    end;
  if tot=0
    then begin
           inc(f1);
           f[f1]:=x;
         end
    else belong[x]:=tot;
end;

begin
  readln(n);
  for i:=1 to n do
  begin
    read(x);
    while x>0 do
    begin
      add(i,x);
      read(x);
    end;
    readln;
  end;
  fillchar(v,sizeof(v),true);
  for i:=1 to n do
    if v[i] then dfs(i);
  e1:=e;
  fillchar(last,sizeof(last),0);
  for i:=1 to e do
    add(side[i].y,side[i].x);
  fillchar(v,sizeof(v),true);
  for i:=n downto 1 do
    if v[f[i]] then
    begin
      inc(tot);
      dfs(f[i]);
    end;
  for i:=1 to e1 do
    with side[i] do
      if belong[x]<>belong[y] then
      begin
        inc(cd[belong[x]]);
        inc(rd[belong[y]]);
      end;
  for i:=1 to tot do
  begin
    if rd[i]=0 then inc(s1);
    if cd[i]=0 then inc(s2);
  end;
  writeln(s1);
  if tot=1 then
  begin
    s1:=0;
    s2:=0;
  end;
  if s1>s2
    then writeln(s1)
    else writeln(s2);
end.


你可能感兴趣的:(poj 1236 Network of Schools 强联通分量Kosaraju)