[经典题目]树的重量

〖题目〗

树的重量

【问题描述】

树可以用来表示物种之间的进化关系。一棵"进化树"是一个带边权的树,其叶节点表示一个物种,两个叶节点之间的距离表示两个物种的差异。现在,一个重要的问题是,根据物种之间的距离,重构相应的"进化树"

N={1..n},用一个N上的矩阵M来定义树T。其中,矩阵M满足:对于任意的ijk,有M[i, j] + M[j, k]<=M[i, k]。树T满足:

1、叶节点属于集合N

2、边权均为非负整数;

3dT( i, j )=M[ i, j ] ,其中dTi, j)表示树上ij的最短路径长度。

如下图,矩阵M描述了一棵树。

 

树的重量是指树上的所有边权之和。对于任意给出的合法矩阵M,它所能表示树的重量是惟一确定的,不可能找到的两棵不同重量的树,它们都符合矩阵M。你的任务就是,根据给出的矩阵M,计算M所表示树的重量。下图是上面给出的矩阵M所能表示的一棵树,这棵树的总重量为15

 

【输入格式】weight.in

输入数据包含若干组数据。每组数据的第一行是一个整数n2<n<30)。其中n-1行,给出的是矩阵M的一个上三角(不包含对角线),矩阵中所有元素是不超过100的非负整数。输入数据保证合法。

输入数据以n=0结尾。

 

【输出格式】weight.out

对于每组输入,输出一行,一个整数,表示树的重量。

 

【输入样例】

5

5 9 12 8

8 11 7

5 1

4

4

15 36 60

31 55

36

0

 

【输出样例】

15

71

 

【限制】 内存:50 M  时间:1 S

【评测方式】

【数据规模】

 

 

〖分析〗

首先,先证明一个定理:已知4个点Q1Q2Q3K,并且Q1Q2Q3之间的路径经过Q0,而且知道它们之间的路径的长度(在树中,任意两点间有且仅有一条路径),且则可以求出Q1Q2Q3K点的长度。

证明:

 Q1KQ2的路径长度为M12

   Q1KQ3的路径长度为M13

    Q2KQ3的路径长度为M23

Q1K的路径长度为D1

   Q2K的路径长度为D2

    Q3K的路径长度为D3

 

由“在树中,任意两点间有且仅有一条路径”得:

D2+D3=M23

   Q1KQ2Q1KQ3有相同路径Q1K

   D2=M12-D1

D3=M13-D1

D2-D3 =M12-M13

∴由①②得D2=M23+M12-M13/2

同理,可以计算出D1D3的值,

∴原定理得证。

既然证明了这个命题,问题就迎刃而解了。先建立一个点K,连接K与其他点之间的连线。然后利用上面这个定理可以求出每个点到K点的最小权。接下来将原最短路径表M中的Mij自减去点iK的最小权和点jK的最小权,并且将calc自加所有点到K的最小权。这样构成了一个新的最短路径表M2

利用表M2K进行拓展(把一个点拓展为一棵树),重复上面过程直到K不能拓展或只能拓展成一条边为止。最后calc自加上这条边的权值或0。则calc就是最后的答案。

本来以为这样就可以通过了。结果只过了一半的点。为什么?就是因为没有对M2进行处理。如果两个点的最短路径为0,就意味着这两个点重合了。这个问题将会使M2无法进行进一步的改进,自然就会产生错误。

其实解决这个问题还是比较简单的,用染色法。若两个点的最短路径为0,则染同一种颜色。当然,在染色之前需要进行一次传递闭包,这样可以减少很多的计算量。

染完色,就是处理。建立新的表M3,其中第i行第j列的值就是上了颜色i与上了颜色j的点之间的路径(这条路径是唯一的)。

M3代替M2,进行计算。

不过这样还不能全对。因为还有一组BT数据:

4

2 13 13

15 15

0

这意味着刚开始就有两个点重复。所以应该是先染一次色。

到这里,就AC了。

〖程序〗

// TASK: weight

var

  m,mm:array [0..30,0..30] of longint;

  c:array [0..30] of longint;

  n:longint;

  f:text;

  d:array [0..30] of longint;

 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;

 function init:boolean;

 var

   i,j,k:longint;

 begin

   readln(f,n);

   if n<=0 then exit(false);

   fillchar(m,sizeof(m),0);

   for i:=1 to n-1 do

     for j:=i+1 to n do

     begin

       read(f,m[j,i]);

       m[i,j]:=m[j,i];

     end;

   exit(true);

 end;

 procedure draw;

 var

   i,j,k,ii,jj,kk,ln:longint;

   ls:longint;

 begin

     fillchar(c,sizeof(c),0);

     ln:=0;

     for i:=1 to n do

       if c[i]<=0 then

       begin

         inc(ln);

         c[i]:=ln;

         for j:=1 to n do

           if m[i,j]<=0 then

             c[j]:=ln;

       end;

     mm:=m;fillchar(m,sizeof(m),0);

     for i:=1 to ln-1 do

     begin

       for ii:=1 to n do

         if c[ii]=i then

         begin

           for j:=i+1 to ln do

             for jj:=1 to n do

               if c[jj]=j then

               begin

                 m[i,j]:=mm[ii,jj];

                 m[j,i]:=mm[ii,jj];break;

               end;

           if m[i,j]>0 then break;

         end;

     end;

     n:=ln;

 end;

 function solve:longint;

 var

   i,j,k,ii,jj,ln:longint;

   ls:longint;

   sum:longint;

 begin

   sum:=0;

   for i:=1 to n do

     d[i]:=maxint shl 10;

   for k:=1 to n do

     for i:=1 to n do

       if m[i,k]>0 then

         for j:=1 to n do

           if (m[i,j]>0)and(m[j,k]>0) then

             d[i]:=min(d[i],(m[i,j]+m[i,k]-m[j,k])div 2);

   for i:=1 to n do

     if d[i]>=maxint then

       d[i]:=0;

   for i:=1 to n do

     inc(sum,d[i]);

   for i:=1 to n do

     for j:=1 to n do

       if i<>j then

         dec(m[i,j],d[i]+d[j]);

   exit(sum)

 end;

 function calc:longint;

 var

   i,j,k,ii,jj,ln:longint;

   ls:longint;

 begin

   calc:=0;draw;

   while true do

   begin

     k:=solve;

     if k<=0 then break;

     inc(calc,k);

     draw;

   end;

   for i:=1 to n do

     for j:=1 to n do

       if m[i,j]>0 then

       begin

         inc(calc,m[i,j]);

        exit;

       end;

 end;

begin

  assign(f,'weight.in');reset(f);

  assign(output,'weight.out');rewrite(output);

  while init do

    writeln(calc);

  close(f);close(output);

end.

 

posted @ 2009-07-20 19:05  为美好世界献上珂学  阅读(163)  评论(0编辑  收藏  举报