最小生成树算法

 

http://hi.baidu.com/mfs666/blog/item/3be684cb5508bffa53664f72.html

最小生成树是在图中构造一棵无根树,使所有点连同,且树的边权的和最小。一般是针对无向图的,如果是有向图的话,怎么处理?这个东西的应用一般就是顶以上的,如果用到应该很容易看出来。对于动态处理边的联机算法,貌似可以使用二叉堆来做卡鲁斯卡尔,或者是每次加入边时找出环,枚举来拆掉环中的边,貌似需要寻找环,用深搜?最近公共祖先?

program primandkrskr;
const
   maxn=500;
   maxp=3000;
type by=record //边表类型,用于克鲁斯卡尔
         r,a,b:longint;u:boolean;//分别是边权,两端和使用标志
         end;
var
   b:array[0..maxp] of by;
   m,s:array[0..maxn,0..maxn] of longint;
   tr:array[0..maxn] of longint;
   n,p,i,j,t,tt:longint;

function prim(x:longint):longint;//普利姆算法
   var
    dist,port:array[0..maxn] of longint;//离树的距离和接入树的点
    i,j,mm,mt:longint;
   begin
    prim:=0;//初始化代价
    for i:=1 to n do begin//初始化,将每个点离树的距离设为离初始点的距离(直接)(无法直接到达的是正无穷)
     dist[i]:=m[x,i];
     port[i]:=x;
    end;
   for j:=1 to n-1 do begin //加入N-1个点
    mm:=maxlongint;//每次要初始化比较变量
    for i:=1 to n do//枚举每个点
     if (dist[i]<>0) and (dist[i]<mm) then begin//如果没有加入(离树的距离大于0),寻找离树最近的
      mm:=dist[i];
      mt:=i;
     end;
    inc(prim,mm);//代价增加这个距离
    dist[mt]:=0;//加入树,离树距离变成0
    for i:=1 to n do//松弛每个点
     if m[mt,i]<dist[i] then begin//如果从新加入的点“直接去”某个点小于某个点到树的距离,则更新距离和接入点
      dist[i]:=m[mt,i];
      port[i]:=mt;
     end;
   end;
   for i:=1 to n do
    tr[i]:=port[i];
   end;

function krskr(x:longint):longint;//克鲁斯卡尔算法,基于排序和并查集,时间复杂度约为O(eLoge),在稀疏图中很快
   var
    f:array[0..maxn] of longint;//并查集数组
    i,j,c,ct:longint;
   function find(x:longint):longint;//并查集查找
    begin
     if f[x]=x then begin
      find:=x;
      exit;
     end;
     find:=find(f[x]);
     f[x]:=find;
    end;
   procedure com(x,y:longint);//并查集合并
    var
     fx,fy:longint;
    begin
     fx:=find(x);fy:=find(y);
     if fx<>fy then
      f[fx]:=fy;
    end;
   procedure qs(s,e:longint);//快速排序
    var
     i,j,m:longint;
     t:by;
    begin
     i:=s;j:=e;m:=b[(i+j) shr 1].r;
     repeat
      while b[i].r<m do inc(i);
      while b[j].r>m do dec(j);
      if i<=j then begin
       t:=b[i];
       b[i]:=b[j];
       b[j]:=t;
       inc(i);dec(j);
      end;
     until i>j;
     if i<e then qs(i,e);
     if j>s then qs(s,j);
    end;
   begin
    for i:=1 to n do//并查集初始化
     f[i]:=i;
    qs(1,p);//对边进行排序
    ct:=0;//初始化已加入的边数
    krskr:=0;//初始化代价
    for i:=1 to p do begin//枚举排序好的边
     if ct=n-1 then//如果已经完成生成树则退出
      break;
     if find(b[i].a)<>find(b[i].b) then begin//如果并查集不在同一集合中,说明加入边不会形成环
      inc(ct);//增加边数
      b[i].u:=true;//记录该边
      com(b[i].a,b[i].b);//合并便两端的点所在集合
      inc(krskr,b[i].r);//增加代价
     end;
    end;
   end;
begin
   readln(n,p);
   for i:=1 to n do begin
    for j:=1 to n do
     m[i,j]:=maxlongint;
    m[i,i]:=0;
   end;
   for i:=1 to p do begin
    readln(t,tt,j);
    m[t,tt]:=j;
    m[tt,t]:=j;
    b[i].r:=j;
    b[i].a:=t;
    b[i].b:=tt;
   end;
   writeln(prim(1));
   writeln(krskr(1));
end.

posted @ 2010-05-14 13:44  jesonpeng  阅读(191)  评论(0)    收藏  举报