zoukankan      html  css  js  c++  java
  • [luogu p1185] 绘制二叉树

    传送门

    绘制二叉树

    题目描述

    二叉树是一种基本的数据结构,它要么为空,要么由根节点,左子树和右子树组成,同时左子树和右子树也分别是二叉树。

    当一颗二叉树高度为(m-1)时,则共有(m)层。除(m)层外,其他各层的结点数都达到最大,且结点节点都在第(m)层时,这就是一个满二叉树。

    现在,需要你用程序来绘制一棵二叉树,它由一颗满二叉树去掉若干结点而成。对于一颗满二叉树,我们需要按照以下要求绘制:

    1、结点用小写字母"o"表示,对于一个父亲结点,用"/"连接左子树,同样用""连接右子树。

    2、定义([i,j)]为位于第(i)行第(j)列的某个字符。若([i,j])为"/",那么([i-1,j+1])([i+1,j-1])要么为"o",要么为"/"。若([i,j])为"",那么([i-1,j-1])([i+1,j+1])要么为"o",要么为""。同样,若([i,j])为第(1-m)层的某个节点(即"o"),那么([i+1,j-1])为"/",([i+1,j+1])为""。

    3、对于第(m)层节点也就是叶子结点,若两个属于同一个父亲,那么它们之间(由3)个空格隔开,若两个结点相邻但不属于同一个父亲,那么它们之间由(1)个空格隔开。第(m)层左数第(1)个节点之前没有空格。

    最后需要在一颗绘制好的满二叉树上删除(n)个结点(包括它的左右子树,以及与父亲的连接),原有的字符用空格替换(ASCII 32,请注意空格与ASCII 0的区别(若用记事本打开看起来是一样的,但是评测时会被算作错误答案!))。

    输入输出格式

    输入格式

    (1)行包含(2)个正整数(m)(n),为需要绘制的二叉树层数已经从(m)层满二叉树中删除的结点数。

    接下来(n)行,每行两个正整数,表示第(i)层第(j)个结点需要被删除($1

    输出格式

    按照题目要求绘制的二叉树。

    输入输出样例

    输入样例 #1

    2 0
    

    输出样例 #1

      o
     / 
    o   o
    

    输入样例 #2

    4 0
    

    输出样例 #2

               o
              / 
             /   
            /     
           /       
          /         
         o           o
        /          / 
       /          /   
      o     o     o     o
     /    /    /    / 
    o   o o   o o   o o   o
    

    输入样例 #3

    4 3
    3 2
    4 1
    3 4
    

    输出样例 #3

               o
              / 
             /   
            /     
           /       
          /         
         o           o
        /           /
       /           /
      o           o
                / 
        o       o   o
    

    说明

    (30\%)的数据满足:(n=0)

    (50\%)的数据满足:(2≤m≤5)

    (100\%)的数据满足:(2≤m≤10,0≤n≤10)

    分析

    此题是一道比较有质量的题。

    一开始写的分治,结果输出非常鬼畜,调了好几天都无果,无奈,参照了题解中的方法直接写的。

    我参照的是 KHIN 神的这篇题解。此篇题解几乎和KHIN神的一模一样。

    首先,定义 (r_i) (root) 为 (m = i) 时根节点的位置(从 (0) 计数),那么就会有

    [r_i = egin{cases} 0 & i = 1 \ 2 & i = 2 \ 2r_{i - 1} + 1 & i > 2end{cases} ]

    我们可以考虑简化一下这个式子。首先,找规律能推出,(forall 1 le k le i - 2),有

    [r_i = 2^kr_{i - k} + 2 ^ k - 1 ]

    (k = i - 2),则有

    [egin{aligned}r_i &= 2^{i - 2}r_2 + 2 ^ {i - 2} - 1 \ &= 2 ^ {i - 2} imes 2 + 2 ^ {i - 2} - 1 \ &= 2 ^ {i - 1} + 2 ^ { i - 2} - 1 \ &= 2^i - 2 ^ {i - 2} - 1end{aligned} ]

    也就是说,(forall i ge 2),有 (r_i = 2^i - 2 ^ {i - 2} - 1)。不知道你是否有发现,这个东西恰好是一层中的宽度(也就是该层节点个数)。

    那么,每个点到父亲节点的距离是什么呢?不难发现,该距离恰好跳过了该点和父节点间子树的宽度,跟 (r_i) 其实是一个东西。也就是说:

    定义 (e_i) (edge)一条 下方有 (i) 个节点的边,该边长度应为:

    [e_i = egin{cases} 1 & i = 1 \ r_i & i > 1 end{cases} ]

    不考虑删除,每次记录输出时每行边,点的位置,输出后,判断子树方向,若左子树则位置自减,右子树则位置自增。节点判断,更新数组。(本篇题解运用的是滚动数组,滚动输出。)

    而一个节点或边的删除与否,我们直接用一个 isErased 数组记录。从上向下逐个扫描,如果父节点被检测到删除或者本身这个节点就被删除了,那么就可以isErased数组记录。输出时,如果这个位置被擦除了,那么直接输出空格即可。

    上代码咯。

    代码

    /*
     * @Author: crab-in-the-northeast 
     * @Date: 2020-08-14 15:00:06 
     * @Last Modified by: crab-in-the-northeast
     * @Last Modified time: 2020-08-14 22:10:56
     */
    
    //之前写的解法太辣鸡,且死活查不出错,这里参照了类似于KHIN神的方法。
    //由神仙xk帮忙debug,并不是抄袭。qwq
    //链接:https://www.luogu.com.cn/blog/236807/Solution-luogu-P1185
    //顺便 % 一下KHIN神
    #include <iostream>
    #include <cstdio>
    
    const int maxn = 12;
    const int maxm = 12;
    
    bool isErased[maxm][1 << maxm];
    int pos[2][1 << maxm];
    
    int main() {
        int n, m;
        std :: scanf("%d%d", &m, &n);
        
        pos[0][0] = (1 << m) - (1 << m - 2);
    
        for (int i = 0; i < n; ++i) {
            int x, y;
            std :: scanf("%d%d", &x, &y);
            isErased[x - 1][y - 1] = true;
        }
    
        for (int i = 1; i < m; ++i)
            for (int j = 0; j < 1 << i; ++j)
                if(isErased[i - 1][j >> 1])
                    isErased[i][j] = true;
    
        for (int i = 1; i < pos[0][0]; ++i, putc(' ', stdout));
        puts("o");
        for (int i = 1; i < m; ++i) {
            for (int j = 0; j < 1 << i; ++j)
                pos[1][j] = pos[0][j >> 1] + (j & 1 ? 1 : -1);
            std :: swap(pos[0], pos[1]);
    
            for (int k = 1; k < std :: max((1 << m - i) - (1 << m - i - 2), 2) ; ++k) {
                for (int j = 1, l = 0; l < 1 << i; ++j)
                    if (j == pos[0][l]) {
                        putchar(isErased[i][l] ? ' ' : (l & 1 ? '\' : '/'));
                        pos[0][l] += l & 1 ? 1 : -1;
                        ++l;
                    } else
                        putchar(' ');
                puts("");
            }
    
            for (int j = 1, k = 0; k < 1 << i; ++j)
                putchar(j == pos[0][k] && !isErased[i][k++] ? 'o' : ' ');
            puts("");
        }
        return 0;
    }
    

    评测记录

    评测记录

  • 相关阅读:
    hdu 3342 Legal or Not 拓排序
    hdu 1596 find the safest road Dijkstra
    hdu 1874 畅通工程续 Dijkstra
    poj 2676 sudoku dfs
    poj 2251 BFS
    poj Prime Path BFS
    poj 3278 BFS
    poj 2387 Dijkstra 模板
    poj 3083 DFS 和BFS
    poj 1062 昂贵的聘礼 dijkstra
  • 原文地址:https://www.cnblogs.com/crab-in-the-northeast/p/luogu-p1185.html
Copyright © 2011-2022 走看看