Problem : 探索数字迷塔
Time Limit: 1 Sec Memory Limit: 64 MB
Description
晶晶最近迷上了数字迷宫游戏,整天沉浸在一串串看似简单的数字中自得其乐。数字迷宫游戏的魅力体现在变化中隐含着不变的规律,归纳是探究数字迷宫的法宝之一。图10.1-1就是一个由线连接起来的数字小方格组成的数字迷塔。
这个迷塔共n层,它由n×(n+1)/2个小方格组成。每个小方格中都有一个数字,并且连着下一层的两个小方格。现从塔顶走到塔底,每一步只能走到相邻的方格中,则经过方格的数字之和最大值是多少?这个问题晶晶已经琢磨一天了,她感觉异常棘手。你能帮帮她吗?
Input
输入数据共n+1行,第1行是一个整数n(1≤n≤1000),表示数字迷塔的高度,接下来用n行数字表示数字迷塔,其中第i行有i个正整数,且所有的正整数均不大于100。
Output
输出可能得到的最大和。
Sample Input
5
9
12 15
10 6 8
2 18 9 5
19 7 10 4 16
Sample Output
59
HINT
样例说明:9→12→10→18→10
方法策略
对于这个问题,首先我想到的是从特殊到一般的方法,即先考虑一个较少的输入范围,如输入高度为5,然后思考如何用实际方法解决这个问题,再得出一般性的结论。先来分析这个特殊的树,它是有向的图,每个节点(除叶子外)都有两个子节点,而且相邻两个节点还共有一个子节点。很容易把它想象一个楼梯的模型,那么父节点与子结点的位置关系就很清楚了,即子节点在父节点的正下方和右下角,所以遍历这棵树和建立树的结构就迎刃而解了。对于求最大消耗路径问题,考虑到父子节点间的关系,可以采用用动态规划的方法解决,即:
cost(总)= cost(父) + MAX(cost(子左),cost(子右))
找到了递推关系,应该很容易计算出结果了。
具体如何建立这棵树呢?首先,还是用老办法,定义一个结构来表示每个节点(含数据,左、右指针等成员)。然后考虑如何输入数据和初始化所有节点:首先定义一个方法,输入是树的高度,输出是根节点的地址(便于访问树);利用树的高度信息及确定每层所含节点的数目,所以可以直接用动态数组来生成每层的节点,但是同时还要保留该数组的首地址,便于下面访问节点,所以需要创建一个二级指针来记录; 最后用两层循环来让父结点的指针指向子节点即可。
相应代码:
1 /* 2 * numberati.cpp 3 * Copyright 2015 Kevin <[email protected]_WINDOWS8> 4 * 5 * This program is intented to solve the Digit Magic Tower problem. 6 * 7 */ 8 9 #include<iostream> 10 using namespace std; 11 12 /* Definiton of Node with the struct type*/ 13 struct Node{ 14 int data; 15 Node * left; 16 Node * right; 17 }; 18 19 /* Function Declaration */ 20 Node * buildTree(); 21 int findMaxPath(Node * root); 22 int max(int, int); 23 24 /* Drive for */ 25 int main(){ 26 Node * root = buildTree(); 27 cout << findMaxPath(root); 28 return 0; 29 } 30 31 /* Utility function to build a tree */ 32 Node * buildTree(){ 33 int height=1; 34 cin >> height; 35 36 // Make space to hold the value 37 Node ** temp=new Node *[height]; 38 for (int i = 0; i < height; i++){ 39 temp[i] = new Node[i + 1]; // for every column, use a pointer to access the elements 40 for (int j = 0; j <= i; j++){ 41 cin >> temp[i][j].data; 42 temp[i][j].left = NULL; 43 temp[i][j].right = NULL; // Intilize the pointers 44 } 45 } 46 47 // To link the elements, make pointers point their children 48 for (int i = 0; i<height-1; i++){ 49 for (int j = 0; j <= i; j++){ 50 temp[i][j].left = &temp[i + 1][j]; 51 temp[i][j].right = &temp[i + 1][j + 1]; 52 } 53 } 54 55 return temp[0]; // return the root node‘s adress 56 } 57 58 /* Utility function to find the max path, and return the final cost */ 59 int findMaxPath(Node * root){ 60 if (root==NULL) // Intilize the pointer before using it. 61 return 0; 62 63 int Max = max(findMaxPath(root->left), findMaxPath(root->right)); 64 return (root->data + Max); 65 } 66 67 /* Get the larger value from two inputs */ 68 int max(int a, int b){ 69 if (a<b) 70 return b; 71 else 72 return a; 73 }
编写代码中出现的问题:
由于之前很久没有用c++,就把c++中一些基本而重要的概念忘记了。例如,在创建一个变量的时候,一定要适当地将它初始化,否则很容易出现运行时错误。特别是指针,由于它直接访问内存,若没有很好的处理,很容易就早成程序意外地终止。在编写这个程序中,我就不幸遇到这个问题,由于没有初始化节点的左右指针,出现了无法读取内存的运行时错误,导致自己调试了很久都没明白错误的来源(ps,也可能自己水平还不够吧)。因为个人觉得这个教训比较重要,在此记录该错误,希望以后能引以为鉴。
用VS2013调试:
注释掉初始化子节点的左右指针:
测试代码:
程序能启动运行
输入数据后运行中断:
调试代码发现:
当访问到叶子节点的指针时出现了错误,data=???,left=???,right=???
最后,去掉注释后,在dos窗口下用g++编译能正常运行: