poj 3177 Redundant Paths 边双连通分量

题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。


分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

缩点后,新图是一棵树,树的边就是原无向图的桥。

现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

结论:添加边数=(树中度为1的节点数+1)/2

具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

求边双连通分量的方法:

Low[u]=Min{

DFN(u),

DFN(v), 存在无向边(u,v) (u,v)不能是搜索树中的边

Low(v)  vu扩展出来的儿子

}

上面的DFN(x)表示x这个结点是第几个入栈的。

dfs完后若low[x]=low[y]则x和y在同一双连通分量内。


一开始跟标程对过无数遍了还是不知道为什么WA,后来在网上看到说可能会有重边,也就是在算节点的度的时候会多算,所以把重边去掉就可以A了。


代码:

var
  n,m,t,sum,e:longint;
  d,low,dfn,last:array[1..5000] of longint;
  v:array[1..5000,1..5000] of boolean;
  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;
  inc(e);
  side[e].x:=y; side[e].y:=x; side[e].next:=last[y]; last[y]:=e;
end;

procedure init;
var
  i,x,y:longint;
begin
  readln(n,m);
  fillchar(v,sizeof(v),true);
  for i:=1 to m do
  begin
    readln(x,y);
    if not v[x,y] then continue;
    v[x,y]:=false;
    v[y,x]:=false; 
    add(x,y);
  end;
end;

function min(x,y:longint):longint;
begin
  if x<y then exit(x)
         else exit(y);
end;

procedure dfs(x,fa:longint);
var
  i:longint;
begin
  inc(t);
  dfn[x]:=t;
  low[x]:=t;
  i:=last[x];
  while i>0 do
    with side[i] do
    begin
      if dfn[y]=0
        then begin
               dfs(y,x);
               low[x]:=min(low[x],low[y]);
             end
        else if y<>fa then low[x]:=min(low[x],dfn[y]);
      i:=next;
    end;
end;

procedure main;
var
  i,ans,j:longint;
begin
  dfs(1,1);
  for i:=1 to e do
    with side[i] do
      if low[x]<>low[y] then inc(d[low[x]]);
  ans:=0;
  for i:=1 to n do
    if d[i]=1 then inc(ans);
  writeln((ans+1) div 2);
end;

begin
  init;
  main;
end.


你可能感兴趣的:(poj 3177 Redundant Paths 边双连通分量)