poj1236 Network of Schools (tarjan缩点+求入度为0的点和出度为0的点的个数)

题目链接:http://poj.org/problem?id=1236
 

题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输

问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。

问题2:至少需要添加几条传输线路(),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。


强连通分量缩点求入度为0的个数和出度为0的分量个数

去掉题目背景就变成了:

给你一个有向图(顶点数<=100),

你需要:

1、至少选几个顶点,使从这些顶点出发能到达所有的点

2、至少加几条边,使从任意一个顶点出发能到达所有的点


解题思路:

1、缩点,形成DAG

2、求出在DAG上,有多少个顶点入度为0(ans1),有多少个顶点出度为0(ans2)


所以,

第一问就变成了:DAG中有几个入度为0的点

第二问要求形成有向图的强连通图,那么第二问就变成了:对于一个DAG,至少加几条边使它变成强连通图(有向)

易知:对于有向图的强连通图中的每一个顶点一定有它的入度和出度都不为0

假设DAG中我们有n个入度为0的点,m个出度为0的点,

为了让每一个点入度都不为0,我们把这n个点每一个点都连入一条边;

为了让每一个点出度都不为0,我们把这m个点每一个点都连出一条边;

同时,要求加边最少

显然,我们只需要从出度为0的点连出一条边同时把它连入入度为0的点,形成一一对应,即最优选择

情况如下:

1、m=n 我们只需要连接m条边即可

2、m>n 按照上面的最优选择,我们加了n条边,还剩下(m-n)个出度为0的点,对于这些点我们每一个都连出一条边,所以一共有m条边

3、m<n 按照上面的最优选择,我们加了m条边,还剩下(n-m)个入度为0的点,对于这些点我们每一个都连入一条边,所以一共有n条边

综上所述:对于一个DAG,至少加 max(n,m)几条边使它变成强连通图(有向)


显然,第一问的答案为ans1,第二问的答案为max(ans1,ans2)

注意:对于一个本身就是强连通图的,显然第一问答案是1,第二问答案是0。

     但是由于缩点后形成单独的一个点,所以第二问会输出1,要特判出这种情况

var
        n,l,ans1,ans2,x :longint;
        i               :longint;
        t,p,q           :longint;
        last            :array[0..110] of longint;
        ru,chu          :array[0..210] of longint;
        pre,other       :array[0..10010] of longint;
        top,time        :longint;
        dfn,low,belong,z:array[0..110] of longint;
        flag            :array[0..110] of boolean;
function max(a,b:longint):longint;
begin
   if a>b then exit(a) else exit(b);
end;

function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

procedure dfs(x:longint);
var
        p,q,cur:longint;
begin
   inc(time);
   dfn[x]:=time;
   low[x]:=time;
   inc(top);
   z[top]:=x;
   flag[x]:=true;
   //
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if dfn[p]=0 then
      begin
         dfs(p);
         low[x]:=min(low[x],low[p]);
      end else
      if flag[p] then low[x]:=min(low[x],dfn[p]);
      q:=pre[q];
   end;
   //
   if dfn[x]=low[x] then
   begin
      cur:=-1;inc(t);
      while (cur<>x) do
      begin
         cur:=z[top];
         dec(top);
         belong[cur]:=n+t;
         flag[cur]:=false;
      end;
   end;
end;

procedure connect(x,y:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
end;

begin
   read(n);
   l:=0;ans1:=0;ans2:=0;t:=0;time:=0;top:=0;
   for i:=1 to n do
   begin
      read(x);
      while (x<>0) do
      begin
         connect(i,x);
         read(x);
      end;
   end;
   //
   for i:=1 to n do if dfn[i]=0 then dfs(i);
   //
   for i:=1 to n do
   begin
      q:=last[i];
      while (q<>0) do
      begin
         p:=other[q];
         if (belong[p]<>belong[i]) then
         begin
            inc(chu[belong[i]]);
            inc(ru[belong[p]]);
         end;
         q:=pre[q];
      end;
   end;
   //
   for i:=1 to t do
   begin
      if ru[n+i]=0 then inc(ans1);
      if chu[n+i]=0 then inc(ans2);
   end;
   //
   if (t=1) then
   begin
      writeln(1);
      writeln(0);
   end else
   begin
      writeln(ans1);
      writeln(max(ans1,ans2));
   end;
end.
              ——by Eirlys

转载请注明出处=w=



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值