本文介绍北京大学ACM网站2567号题目的解法。介绍部分基本翻译自网站上的题目介绍。

题目介绍:
   给定一棵各节点编号为整数1,2,3...n的树(例如,无环连通图),其Prufer编码(Prufer code,不知道有没有标准的译法,用金山词霸没有查到,用Google也没有搜索到)构造方法如下:从树中去掉编号值最小的叶子节点(仅与一条边邻接的节点),以及与它邻接的边后,记下与它邻接的节点的编号。在树中重复这个过程,知道只剩下一个节点(总是编号为n的节点)为止。记下的n-1个编号序列就是树的Prufer编码。你的任务是计算给定的树的Prufer编码。树用下列语法表示:
  T-->(N S)
  S--> T S | empty
  N-->number
就是说,树由一对括号包围一个表示根节点的数,以及其后跟随的任意多个(可能没有)由单个空格分隔的子树组成。(这里的表示方法类似编译原理中的产生式)根据这个定义,树的根节点也可能是一个叶子节点。
输入:
   由多个测试案例组成。每个案例用输入文件的一行以上述格式表示一棵树。EOF表示输入结束。可以假定1 <= n <= 50。
输出:
   对于每个测试案例产生包含给定树的Prufer编码的单行输出。数之间由单个空格分隔。不得在行末尾添加任何空格。
示例输入:
(2 (6 (7)) (3) (5 (1) (4)) (8))
(1 (2 (3)))
(6 (1 (4)) (2 (3) (5)))
示例输出:
5 2 5 2 6 2 8
2 3
2 1 6 2 6
 
程序:
1   
2    #include
3    #include
4    #include
5   
6    #define  END        0  // 结束
7    #define  L_BRACKET  1  // 左括号
8    #define  R_BRACKET  2  // 右括号
9    #define  NUMBER     4  // 数
10  
11   typedef struct _node
12   {
13      int value;  // 节点值
14      int childs; // 子节点数
15      struct _node *parent;      // 父节点
16      struct _node *first_child; // 第一个孩子节点
17      struct _node *sibling;     // 下一个兄弟节点
18   }NODE;
19  
20   typedef struct _token
21   {
22      int type;  // 标记类型(左、右括号,数)
23      int value; // 值(仅当类型为数时有效)
24   }TOKEN;
25  
26   typedef void (*FUNC)(NODE *);
27   static NODE *pSmallest = 0;// 指向最小的叶子节点
28  
29  
34   char *GetNextToken(char *input,TOKEN *pToken)
35   {
36      char *p,ch;
37      int value;
38  
39      pToken->type = END;
40     
41      p = input;
42      while ((ch = *p) && ch != '(' && ch != ')' && !isdigit(ch))
43          p++;
44      switch(ch)
45      {
46      case '(': pToken->type = L_BRACKET; p++; break;
47      case ')': pToken->type = R_BRACKET; p++; break;
48      default:
49         
50          if (isdigit(ch))
51          {
52              value = 0;
53              while ((ch = *p) && isdigit(ch))
54              {
55                  value = 10 * value + (ch - '0');
56                  p++;
57              }
58              pToken->type  = NUMBER;
59              pToken->value = value;
60          }
61          break;
62      }
63     
64      return (*p == '\0' ? 0 : p);
65   }
66  
67  
71   NODE *BuildTree(char *input)
72   {
73      char *p;
74      TOKEN token;
75      NODE  *pCur,*pNew,*pRoot,*pLast;
76  
77      p = input; pCur = pLast = pRoot = 0;
78      do
79      {
80          p = GetNextToken(p,&token);
81          switch(token.type)
82          {
83          case L_BRACKET:
84              break;
85          case R_BRACKET:
86             
87              pLast = pCur;
88              if (0 != pCur)
89                  pCur = pCur->parent;
90              break;
91          case NUMBER:
92              pNew = (NODE *)malloc(sizeof(NODE));
93              memset(pNew,0,sizeof(NODE));
94              pNew->value = token.value;
95             
96              pNew->parent = pCur;
97              if (0 != pLast)
98              {
99                  pLast->sibling = pNew;
100                 pLast = pNew;
101             }
102             if (0 != pCur)
103             {
104                 if (0 == pCur->childs++)
105                     pCur->first_child = pNew;
106             }
107             else pRoot = pNew;
108 
109             p = GetNextToken(p,&token);
110             if (token.type == L_BRACKET)
111             {
112                 pCur = pNew;
113                 pLast = 0;
114             }
115             else if (token.type == R_BRACKET)
116                 pLast = pNew;
117             break;
118         }
119     }while (0 != p);
120     return pRoot;
121  }
122 
123 
127  void TravelTree(NODE *pRoot,FUNC func)
128  {
129     NODE *pCur;
130     if (0 == pRoot) return;
131     (*func)(pRoot);
132     pCur = pRoot->first_child;
133     while (pCur)
134     {
135         TravelTree(pCur,func);
136         pCur = pCur->sibling;
137     }
138  }
139 
140 
141  int IsLeafNode(NODE *pNode)
142  {
143     return (0 != pNode && (0 == pNode->childs && 0 != pNode->parent)
144     || (1 == pNode->childs && 0 == pNode->parent));
145  }
146 
147 
148  void LeafValueCompare(NODE *pNode)
149  {
150     if (IsLeafNode(pNode))
151         if (0 == pSmallest) pSmallest = pNode;
152         else if (pNode->value < pSmallest->value)
153             pSmallest = pNode;
154  }
155 
156 
161  NODE *ReleaseLeafNode(NODE *pRoot,NODE *pNode)
162  {
163     NODE *pParent,*pSibling;
164     if (0 == pNode->childs)
165     {
166         pParent = pNode->parent;
167         if (pParent)
168         {
169             pSibling = pParent->first_child;
170             if (pNode == pSibling)
171                 pParent->first_child = pNode->sibling;
172             else
173             {
174                 while (pSibling && pSibling->sibling != pNode)
175                     pSibling = pSibling->sibling;
176                 if (pSibling)
177                     pSibling->sibling = pNode->sibling;
178             }
179             pParent->childs--;
180         }
181         free(pNode);
182     }
183     else
184     {
185         pRoot = pNode->first_child;
186         pRoot->parent = 0;
187         free(pNode);
188     }
189     return pRoot;
190  }
191 
192  void ReleaseTree(NODE *pRoot)
193  {
194     NODE *pCur,*pNext;
195     if (pRoot)
196     {
197         pCur = pRoot->first_child;
198         while (pCur)
199         {
200             pNext = pCur->sibling;
201             ReleaseTree(pCur);
202             pCur = pNext;
203         }
204         free(pRoot);
205     }
206  }
207 
208  int main(int argc,char *argv[])
209  {
210     NODE *pRoot;
211     char line[250];
212     int  n;
213     while (!feof(stdin))
214     {
215         line[0] = '\0';
216         gets(line);
217         pRoot = BuildTree(line);
218         n = 0;
219         while (pRoot && pRoot->childs != 0)
220         {
221             pSmallest = 0;
222             TravelTree(pRoot,LeafValueCompare); // 遍历树
223             if (0 == pSmallest->childs)
224                 printf(n++ ? " %d" : "%d",pSmallest->parent->value);
225             else
226                 printf(n++ ? " %d" : "%d",pSmallest->first_child->value);
227             pRoot = ReleaseLeafNode(pRoot,pSmallest);
228         }
229         if (0 != pRoot)
230         {
231             printf("\n");
232             ReleaseTree(pRoot);
233         }
234     }
235     return 0;
236  }
237 
 
说明:
1 解决此问题的第一个难点就在于如何利用输入的一行数据建立树。程序中BuildTree()函数用于建立树。函数中pCur表示当前处理的树层次父节点,当前层的节点都是它的孩子节点;pLast表示最近处理的一个节点,新建的节点将成为它的兄弟节点,放在其右边;pRoot当然就表示根节点了。BuildTree()读入一行中的各种Token进行处理,直到一行处理完成。
(1)第85至90行:遇到右括号时表示某子树结束,返回到上一层。因为子树也是树,而根据上面的定义T-->(N S)知道,右括号是树的结束符(编译原理里面有专门的术语的,不记得是不是“结束符”)。遇到右括号就表示某子树处理完成了,需要返回到上一级,处理这个子树的下一个右兄弟节点(可能是某子树的根)
(2)第92到94行:遇到一个数时,建立一个它所表示的新的树节点。
(3)第95到107行:设定父兄节点。直接看这段代码还是不太好理解的,看下面这张示意图就好理解了:
(4)第109到116行:根据下一个Token类型决定做什么动作,这个好像也跟编译原理里的某个理论有关系,也记不清楚了。这里我看代码115行时候有点疑惑了:如果下一个符号既不是左括号,又不是右括号呢?仔细想想,根据上面的语法定义,数后面是不会跟随一个数的(在编译原理上好像所谓的“尾随符号”就是表示这个的,在书上叫做follow符号集)
 
2 树建立好之后的任务就是生成Prufer编码。我采用的方法比较笨:不停地遍历树,每次遍历找到编号最小的叶子节点,输出它的父节点的编号,将它从树中去掉,直到树只剩下一个节点。
(1)127到138行:对树进行前序遍历,采用的是递归的方法。不过在遍历的之前先调用148行的LeafValueCompare()函数,来记录编号最小的叶子节点。
(2)141到145行:IsLeafNode()函数判断节点是不是叶子节点。注意最后一个条件:(1 == pNode->childs && 0 == pNode->parent)。这就是上文说的,树的根节点也可能是叶子节点。也就是树根节点在没有任何孩子节点时就也是叶子节点。
   关于生成Prufer编码,我想应该有效率更高的方法:只进行一次遍历,记下各个节点的编号值以及节点指针,按照节点编号从小到大进行排序;以后从小到大进行搜索,搜索到一个叶子节点就把它去掉,记下其父节点编号。直到树只剩下一个节点。

【转】ACM 2567 -- 树的Prufer编码的更多相关文章

  1. BZOJ1005--[HNOI2008]明明的烦恼(树的prufer编码)

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5768  Solved: 2253[Submit][Stat ...

  2. 树的Prufer 编码和最小生成树计数

      Prufer数列 Prufer数列是无根树的一种数列.在组合数学中,Prufer数列由有一个对于顶点标过号的树转化来的数列,点数为n的树转化来的Prufer数列长度为n-2.它可以通过简单的迭代方 ...

  3. 树的prufer编码

    prufer是无根树的一种编码方式,一棵无根树和一个prufer编码唯一对应,也就是一棵树有唯一的prufer编码,而一个prufer编码对应一棵唯一的树. 第一部分:树编码成prufer序列. 树编 ...

  4. 【51NOD1806】wangyurzee的树(Prufer编码,容斥原理,组合计数)

    题意:有n个点和m条限制,每条限制限制了一个点的度数不能为某个数. 求合法的树的个数模10^9+7 n<=10^6 m<=17 思路:WYZ作业 首先m<=17显然是2^m容斥 枚举 ...

  5. 【Foreign】树 [prufer编码][DP]

    树 Time Limit: 10 Sec  Memory Limit: 256 MB Description Input Output Sample Input 3 2 2 1 Sample Outp ...

  6. 【转】prufer编码

    既然有人提到了,就顺便学习一下吧,来源:http://greatkongxin.blog.163.com/blog/static/170097125201172483025666/ 一个含有n个点的完 ...

  7. 「模拟赛20180406」膜树 prufer编码+概率

    题目描述 给定一个完全图,保证\(w_{u,v}=w_{v,u}\)且\(w_{u,u}=0\),等概率选取一个随机生成树,对于每一对\((u,v)\),求\(dis(u,v)\)的期望值对\(998 ...

  8. Luogu2290 [HNOI2004]树的计数 (组合计数,prufer编码)

    这不prufer编码吗,防爆long long就行了啊 #include <iostream> #include <cstdio> #include <cstring&g ...

  9. Codeforces 1109D. Sasha and Interesting Fact from Graph Theory 排列组合,Prufer编码

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1109D.html 题意 所有边权都是 [1,m] 中的整数的所有 n 个点的树中,点 a 到点 b 的距离 ...

随机推荐

  1. Pytorch 加载保存模型【直播】2019 年县域农业大脑AI挑战赛---(三)保存结果

    在模型训练结束,结束后,通常是一个分割模型,输入 1024x1024 输出 4x1024x1024. 一种方法就是将整个图切块,然后每张预测,但是有个不好处就是可能在边界处断续. 由于这种切块再预测很 ...

  2. PHP下载压缩包文件

    PHP 压缩文件需要用到 ZipArchive 类,Windows 环境需要打开 php_zip.dll扩展. 压缩文件 $zip = new ZipArchive(); // 打开一个zip文档,Z ...

  3. HDU - 2041 - 超级楼梯(dp)

    题意: 有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法? 思路: 如何到第n阶台阶,只能从n-1和n-2台阶上去,那么只需要计算到n-1阶台阶和到n-2阶台 ...

  4. WIndows 系统下的常用命令 和 检测方法

    ### 一.检测硬盘速度(Windows 自带工具) #### 使用windows 系统自带的工具测试硬盘读写速度 > 在使用下面命令前,需要获得管理员权限,才会在Dos窗口上显示(否则,一闪而 ...

  5. with一个对象,自动触发__enter__方法

    class Foo(object): def __init__(self): pass def __enter__(self): print("__enter__") def __ ...

  6. linux 安装 phpstorm 并破解

    下载官方软件linux版phpstrom, 貌似很卡要FQ. 我下载我的百度网盘备用了.解压目录, mv 到/opt/ 下 cd进  bin目录下chmod  777 phpstorm.sh执行 ./ ...

  7. 在vue项目中使用stylus来实现移动端的1px

    1.目录结构(vue项目,但是并不局限于vue) 2.首先定义一个mixin.styl文件 border-1px($color) position: relative &:after disp ...

  8. phpcms 搭建宣传网站首页

    1 .修改后台提交的表单信息展示: 文件路径: phpcms\modules\formguide\template\formguide_info_list.tpl.php function getQu ...

  9. 【Codeforces 1129A】Toy Train

    [链接] 我是链接,点我呀:) [题意] 火车从1,2,3...n->1的方式绕圈走.(即每次从i走到i+1) 有一些点有货物需要装载,但是每个点只能装上去一个货物. 每个货物都有目标点卸货点( ...

  10. [bzoj1607][Usaco2008 Dec]Patting Heads 轻拍牛头_筛法_数学

    Patting Heads 轻拍牛头 bzoj-1607 Usaco-2008 Dec 题目大意:题目链接. 注释:略. 想法:我们发现,位置是没有关系的. 故,我们考虑将权值一样的牛放在一起考虑,c ...