Prufer Code

    这两天看了Prufer编码。意思就是把一棵n个节点并且带编号的无向树与一个n-2长度的数组建立双射。规则如下:

    (1)将树中与编号最小的叶节点相连的节点编号加入数组。

    (2)删去编号最小的叶节点。

    (3)重复第1个操作。直到树中只剩两个节点结束操作。

    这样就得到了一组数。

    通过Prufer编码得到的数组具有这些特性:

    1.一棵树对应唯一的一组数组。

    2.每个节点的标号出现在数组中的次数等于它的度数减1。(原树中的叶子节点不出现在数组中)

    优点:我了解了Prufer编码后,发现,可以对一个数组进行分析从而分析一棵无向树。这样就起到了降维(也许可以这么说吧。。。。。)的作用,可以直接利用组合数学的方法直观求解给定度数的不同无向树的种类。Amazing、、

    那么,对于一个长度为n-2的数组如何还原为一棵n个节点的无向带标号树呢?

    步骤如下:

    (1)确定每个标号所对应的点的度数。

    (2)从数组的头开始,依次向后。把当前数组中存的标号所对应的节点与当前n个节点中标号最小的叶子节点相连。

    (3)重复第二步骤,直到做了n-2遍。

    (4)将剩余的两点相连!

    我弱弱地想:在还原为树的过程中,每一次都要寻找标号最小的叶节点。可以用映射堆优化。

    建立一个堆,存储叶节点,以标号为比较的关键字。在做(2)步骤时,维护堆,同时删除连接的叶子节点。两个操作时间复杂度都为O(logN),询问只要O(1)。

 

    对于给定每个点度数的无向树,求不同树的总数的公式为:

    ans=Cn-2d1-1*Cn-2-d1-1),d2-1*…*Cdn-1dn-1=n-2)!/((d1-1)!*d2-1)!*…*dn-1)!)

 

参考:

桐烟心田 的百度空间。

 

附中oj 1633

计算出一个有N个顶点和K个叶子的有标签的树的数目。

 

 

题解:本题就是计算在n-2个数组单元中放入n-k个不同的数有多少种方法。

 

AC CODE  (抄袭神牛代码的。。。)

 

program fu_1633;
var c,f:array[0..100,0..100] of longint;
    jc:array[0..100] of longint;
    i,j,ans,n,k:longint;
begin
  for i:=1 to 100 do
  begin
    c[i,1]:=i;
    for j:=2 to i do
      c[i,j]:=(c[i-1,j-1]+c[i-1,j]) mod 2007;    //组合数组。
  end;

  jc[0]:=1; f[1,1]:=1; f[0,0]:=1;
  for i:=1 to 100 do jc[i]:=jc[i-1]*i mod 2007;  //阶乘。。。。
  for i:=2 to 100 do
  begin
    for j:=1 to i do
      f[i,j]:=(f[i-1,j-1]+f[i-1,j]*j) mod 2007;    //f[i,j]表示在i个位置中放入j个不同的数有多少种方法。
  end;
  while not(eof) do
  begin
    readln(n,k);
    if n=1 then begin writeln('1'); continue; end;
    ans:=(f[n-2,n-k]*c[n,k]) mod 2007;    //要乘上从n个节点中选出k个叶节点的选法。
    ans:=(ans*jc[n-k]) mod 2007;    //节点是有标号的,要考虑每一种方法中,数组中各个不同数的全排列。。。意会下。。。

    writeln(ans);
  end;
end.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值