2016微软探星夏令营在线技术笔试题解(2)

  题目来源: 
        HihoCoder1342
 

题目描述:
    Let's draw a picture of full binary tree using ASCII characters. In this picture nodes are represented by '#'. A parent node connects its left child by '/' and its right child by '\'.

For the sake of aesthetic, the nodes of the same height must be painted on the same line. And the nodes must be perfectly connected by '/' and '\'. Intersections or misplacements are not allowed.

    For example, this is the full binary tree of height 2:


    This is the full binary tree of height 3:

   

    This is the full binary tree of height 4:

 

    Now we build a Cartesian coordinate system for the picture. We make the root at the origin (0, 0). The positive direction of x is to the bottom and the positive direction of y is to the right.

    The full binary tree of height 2 is illustrated as below.

 

    Given the height of the tree and a rectangle area of the picture, your task is to find out the amount of nodes in such area. For example, assuming the height of tree is 2, the left-top corner of the rectangle area is at (0, 0) and the right-bottom corner is at (2, 2), then the area contains 2 nodes (the root and its right child) totally. 

    题目大意:利用'#'、'/'和'\'三种符号绘制指定层数的满二叉树,在绘制完成的图中定义二维坐标系,指定根节点的坐标为(0,0),然后给出一个矩形区域,判定矩形区域内包含多少个树的节点(即包含多少个#符号)。

解答:
    本题题目描述的内容比较长,但解答的难度也不大。考察了一些计算和数学归纳的能力。为解本题,首先需要对程序绘制的图进行分析。

·图分析:
    通过观察绘制的二叉树的图像,可以发现:整个图像是一个对称的图形。并且如果不考虑字符的不同,图像中每一行的宽度都比前一行多2,整个图形呈阶梯状分布:
      
    图像中第一行只有根节点,宽度为1,第二行,宽度为3,第3行宽度为5.....如果整个图像有H行,那么,记最后一行的宽度为D,那么
D = 1 + 2×(H-1)..............................(1)
图的高度:
    下面我们推导对于高度绘制高度为h的满二叉树的图像所需的行数,可以将其称为图像的“高度”,即为:H(h)。显然,H(1) = 1H(2)=3
    对于高度为h的二叉树的图像的绘制,我们可以理解为下面的过程,绘制两个高度为h-1的满二叉树的图像,将其并排放置后,用一个公共的根节点将两棵子树连接起来,此时两棵子树的最后一层中间有1个字符的间隔:
     
    利用这个特点,可以找到图像中最后一行宽度的递推规律:高度为h的树的图像的最后一行的宽度可以由高度为h-1的树的图像的最后一行宽度计算得到,结果是2倍再加1。即:
D(h) = 2×D(h-1) + 1..........................(2)
根据式(1),可以计算D(h-1):
D(h-1) = 1 + 2×[H(h-1) - 1]...................(3)
同时也可以根据式(1)计算D(h):
D(h) = 1 + 2×[H(h)-1].........................(4)
将式(3)和式(4)代入式(2),然后整理可得H(h)的递推计算公式:
                      
1 + 2×[H(h)-1] = 2×{1 + 2×[H(h-1) - 1]} + 1
                   => H(h) - 1 = 2×[H(h-1) - 1] + 1
                   => H(h) = 2×[H(h-1) - 1] + 2 = 2×H(h-1) 
                   => H(h) = 2×H(h-1)

此时,根据等比数列的计算公式可以得到:
H(h) = H(2)×2^(h-2)............................(5)
H(2)=3, 于是:
H(h) = 3 × 2^(h-2).............................(6)
以上就是计算图高度的公式。这里H(1)=1是一种特殊情况,不可以采用上面的公式计算。

各层节点竖直方向的坐标:
    知道图的高度后,我们还需要知道图中各层节点在竖直方向上的坐标,题目中定义的坐标系竖直方向是X轴,定义根节点的深度是0,从上到下各层节点深度为1、2.....h-1。我们将图像中深度为x的节点的竖直方向坐标记为:X(x)。
    根据题意根节点的坐标为(0,0),所以:X(0) = 0。对于高度为h的树,它的左右孩子都是高度为h-1的子树,而根节点左右孩子的都是深度为1的节点。
    
    而高度为h-1的子树的图像高度为H(h-1),因此根节点到其左右孩子在竖直方向上的间距就是:H(h) - H(h-1) 即:
X(1) - X(0) = H(h) - H(h-1)
根据上文中式(5),可以得到:
X(1) - X(0) = H(h) - H(h)/2 = H(h)/2
以此类推,可以得到:
X(2) - X(1) = H(h-1)/2 = H(h) / (2^2)
X(3) - X(2) = H(h-2)/2 = H(h) / (2^3)
......
    X(x) - X(x-1) = H(h-x+1)/2 = H(h) / (2^x)
累加以上各式,就可以得到:
                            X(x) - X(0) 
                          = H(h)/2 + H(h)/(2^2) + H(h)/(2^3) + ... + H(h)/(2^x)
                          = {H(h)/2 × [1 - 1/(2^x)]} ÷ [1 - 1/2]
                          = H(h) - H(h)/(2^x).........................(7)

    这样我们就求得了每层节点的X坐标。但该公式对于最后一层的结果却不适用,最后一行的结果需要特别处理。

各层节点水平坐标的最小值和最大值:
    然后我们还需要知道的是每层节点水平坐标的最大值和最小值。这个值的求解方式是显而易见的。在我们得知X(x)之后,就可以利用式(1)来求得该层的宽度为:1 + 2 × X(x),而我们知道图像是左右对称的,根节点的水平坐标是0。所以:每一层节点水平坐标的最大值和最小值是互为相反数的。因此,我们只要记录其绝对值即可,记为:Y(x),那么:
Y(x) = [1 + 2X(x) - 1] ÷ 2 = X(x).................(8)
此时,各层节点的水平坐标的最大值和最小值分别是 Y(x) 和 -Y(x)。
 

各层节点的间距
     通过观察图像可以发现:除去最后一层的节点外,其余各层的节点间距都是相同的:
     
    对于每一层节点的个数,是很容易计算的:深度为x的层,节点个数就是2^x个,因此如果求得节点间的间距,就可以求得树中每一个节点的精确坐标,而该间距的计算也是很容易的,深度为x的层宽度为:1 + 2×X(x) 而节点个数为2^x个,因此间距大小为:
     G(x) = {2 × X(x) + 1 - 2^x} ÷ (2^x - 1)...................(9)

总结:
    下面是对于上面计算结果的总结,我们共用到了4条公式:
        图高度:                         H(h) = 3 × 2^(h-2)                  (h>1)  
        各层节点的X坐标:               X(x) = H(h) - H(h)/(2^x)             (0≤x<H(h)-1) 
        各层节点Y坐标绝对值的最大值:   Y(x) = X(x)                          
(0≤x<H(h)-1) 
        各层节点的间隔:                 G(x) = [2X(x) + 1 - 2^x] / (2^x - 1) 
(0≤x<H(h)-1)
    
 其中:h为树的高度;x为节点的深度,根节点深度为0,其余从上到下递增。
     对于 h = 1 以及 x = H(h) - 1 的情况,上面的公式是不适用的,需要做特殊处理。 

·计算:
    在得到上面的公式后,对于题目结果的计算就比较容易了。题目给出了树的高度h和矩形范围的左上角和右下角的坐标(x1,y1)、(x2,y2)。首先根据给定个树高度h,求出H(h),然后对于深度x,依次赋值为0, 1......H(h)-1,检查X(x)是否落在了[x1,x2]范围内,如果是,则检查该层次的所有每一个节点是否落在了[y1,y2]范围内。如果是,则说明该点落在了矩形范围内,需要被计数。每个节点的坐标可以利用Y(x)和G(x)求得。

输入输出格式:
    输入:
    The first line contains two integers N and MN is the height of the full binary tree. M is the number of queries. (1 ≤ N ≤ 20, 1 ≤ M ≤ 100)
Each query consists of 4 integers x1y1x2 and y2. (x1y1) is the left-top corner of the area and (x2,y2) is the right-bottom corner of the area. 
    输出:
             For each query output the amount of nodes in the area.

程序代码:
/****************************************************/
/* File        : HihoCoder1342                      */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-07-21                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

/* 
 * Formulas used in this program: 
 * H(h) = floor[3 * 2^(h-2)]
 * X(x) = H(h) - floor[H(h)/2^x]
 * W(x) = X(x)
 * G(x) = [2*X(x) + 1 - 2^x] / (2^x - 1)
 */

#include<stdio.h>
#include<math.h>

// Input data. 
int N, M;

/*
 * This function computes how many node are in the give rectangle area.
 * Parameters:
 *		@x1 & @y1: The left-top coordinates of the rectangle area.
 *		@x2 & @y2: The right-bottom coordinates of the rectangle area.
 * Returns:
 *		The number of node which are in the given rectangle area.
 */
int compute(int x1, int y1, int x2, int y2) {
	int H = (int) floor(3 * pow(2, N - 2));
	int count = 0;
	
	for(int i = 0; i < N; i++) {
		int X = H - (int) floor(H / pow(2, i));
		if(X >= x1 && X <= x2) {
			if(i < N - 1) {
				int G = (int) ((2 * X + 1 - pow(2, i)) / (pow(2, i) - 1)) + 1;
				count += (int) pow(2, i);
				if(y1 > -1 * X) {
					count -= (y1 + X - 1) / G + 1;
				}
				
				if(y2 < X) {
					count -= (X - y2 - 1) / G + 1;
				}
			} else {
				count += (int) pow(2, i);
				if(y1 > -1 * X) {
					count -= (y1 + X - 1) / 6 * 2 + 1;
					if((y1 + X - 1) % 6 >= 4) {
						count -= 1;
					}
				}
				if(y2 < X) {
					count -= (X - y2 - 1) / 6 * 2 + 1;
					if((X - y2 - 1) % 6 >= 4) {
						count -= 1;
					}
				}
			}	
		} 
	}
	
	return count;
}

/*
 * The main program.
 */
int main(void) {
	
	scanf("%d %d", &N, &M);
	
	for(int i = 0; i < M; i++) {
		int x1, y1, x2, y2;
		scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
		printf("%d\n", compute(x1, y1, x2, y2));
	}
	
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值