zoukankan      html  css  js  c++  java
  • P3884 [JLOI2009]二叉树问题

    题目传送门

    一、理解与感悟

    1、树结点需要由父到子,有时也需要由子到父,所以结构体需要修改如下:

    //树的结构体+存储数组
    struct Node {
        int id;     // 当前结点ID
        int father; // 爸爸
        int left;   // 左结点ID
        int right;  // 右结点ID
        int depth;  // 深度,不用再次dfs去探测
    } t[N];
    
    

    2、题目只说了表示从结点x到结点y(约定根结点为1),这样可不一定是从父到子,也可能是从子到父,需要两头看一下。

    3、建图:如果出题人使坏,可能会给出 A->B,B->A的数据,需要我们记录是否节点已经录入完成,已完成的不能再次处理,防止死循环。
    后来才知道,出题人没那么坏,他给出的数据确实是先是爹,后是儿子,没有例外,良心!

    4、最大深度:这个好办,创建树时就已经逐个+1进行创建,可以遍历一次找出最大的深度即可。

    5、最大宽度:这个也好办,因为树是现成的,我们只需要一个桶来记录每层的个数,找出最大个数就行了。

    6、结点u到结点v间距离
    这个是本题的关键所在!因为题目中说的很含糊,什么到根的边数,根本不是到根,是到它们两个结点的公共最近祖先

    获取两个结点的公共最近祖先有现成的算法:

    //最近公共祖先默认是根结点,就是1,英文简写:lowestCommonAncestor
    int lca(int x, int y) {
        st[x] = 1;                      //把x的节点记录已走过
        while (t[x].father) {           //遍历至根节点
            x = t[x].father;            //更新遍历爸爸
            st[x] = 1;                  //记录已走过
        }
        //遍历至x节点已走过的节点,找到最近公共祖先
        while (!st[y])y = t[y].father;
        return y;
    }
    
    //按题意输出
        cout << (t[u].depth - t[r].depth) * 2 + (t[v].depth - t[r].depth) << endl;
    

    二、C++代码

    #include <bits/stdc++.h>
    
    using namespace std;
    int n, x, y, u, v;
    const int N = 110;
    
    //树的结构体+存储数组
    struct Node {
        int id;     // 当前结点ID
        int father; // 爸爸
        int left;   // 左结点ID
        int right;  // 右结点ID
        int depth;  // 深度,不用再次dfs去探测
    } t[N];
    
    //记录边的关系
    typedef pair<int, int> PII;
    PII a[N];
    
    //是不是使用过
    int st[N];
    
    //用来计某一层的结点个数
    int bucket[N];
    
    //通过递归构建二叉树
    void dfs(int u) {
        //u是老大,找出它的左儿子和右儿子
        for (int i = 1; i < n; i++) {//遍历所有关系
            if (a[i].first == u) {
                //左结点为空,放入左结点
                if (t[u].left == 0) t[u].left = a[i].second;
                    //否则放入右结点
                else t[u].right = a[i].second;
                //修改深度标识
                t[a[i].second].depth = t[u].depth + 1;
                t[a[i].second].father = u;
                //递归创建子树
                dfs(a[i].second);
            }
        }
    }
    
    //最近公共祖先默认是根结点,就是1,英文简写:lowestCommonAncestor
    int lca(int x, int y) {
        st[x] = 1;                      //把x的节点记录已走过
        while (t[x].father) {           //遍历至根节点
            x = t[x].father;            //更新遍历爸爸
            st[x] = 1;                  //记录已走过
        }
        //遍历至x节点已走过的节点,找到最近公共祖先
        while (!st[y])y = t[y].father;
        return y;
    }
    
    int main() {
        //二叉树结点个数
        cin >> n;
        //接下来的n-1行
        for (int i = 1; i < n; i++) {
            //构建二叉树
            cin >> x >> y;
            //记录关系
            a[i] = {x, y};
        }
        //通过递归建树,构建二叉树
        t[1].depth = 1, t[1].id = 1, t[1].father = 0, st[1] = 1;//根结点1
        dfs(1);
    
        //表示求从结点u到结点v的距离
        cin >> u >> v;
    
        //二叉树的深度
        int maxDepth = 0;
        for (int i = 1; i <= n; i++) maxDepth = max(maxDepth, t[i].depth);
        cout << maxDepth << endl;
    
        //二叉树的宽度
        int maxWidth = 0;
        for (int i = 1; i <= n; i++) bucket[t[i].depth]++;
        for (int i = 1; i <= n; i++) maxWidth = max(maxWidth, bucket[i]);
        cout << maxWidth << endl;
    
        //结点u到结点v间距离:结点间距离的定义:由结点向根方向(上行方向)时的边数×2,与由根向叶结点方向(下行方向)时的边数之和。
        //这里有一个最近公共祖先的问题,才能拿到正确答案
        int r = lca(u, v);
        //按题意输出
        cout << (t[u].depth - t[r].depth) * 2 + (t[v].depth - t[r].depth) << endl;
        return 0;
    }
    

    三、防止出题人使坏的代码

    #include <bits/stdc++.h>
    
    using namespace std;
    int n, x, y, u, v;
    const int N = 110;
    
    //树的结构体+存储数组
    struct Node {
        int id;     // 当前结点ID
        int father; // 爸爸
        int left;   // 左结点ID
        int right;  // 右结点ID
        int depth;  // 深度,不用再次dfs去探测
    } t[N];
    
    //记录边的关系
    typedef pair<int, int> PII;
    PII a[N];
    
    //是不是使用过
    int st[N];
    
    //用来计某一层的结点个数
    int bucket[N];
    
    //通过递归构建二叉树
    void dfs(int u) {
        //u是老大,找出它的左儿子和右儿子
        for (int i = 1; i < n; i++) {//遍历所有关系
            if (a[i].first == u && !st[a[i].second]) {
                //标识已使用
                st[a[i].second] = 1;
                //左结点为空,放入左结点
                if (t[u].left == 0) t[u].left = a[i].second;
                //否则放入右结点
                else t[u].right = a[i].second;
                //修改深度标识
                t[a[i].second].depth = t[u].depth + 1;
                t[a[i].second].father = u;
                //递归创建子树
                dfs(a[i].second);
            } else if (a[i].second == u && !st[a[i].first]) {
                st[a[i].first] = 1;
                if (t[u].left == 0) t[u].left = a[i].first;
                else t[u].right = a[i].first;
                t[a[i].first].depth = t[u].depth + 1;
                t[a[i].first].father = u;
                dfs(a[i].first);
            }
        }
    }
    
    //最近公共祖先默认是根结点,就是1,英文简写:lowestCommonAncestor
    int lca(int x, int y) {
        st[x] = 1;                      //把x的节点记录已走过
        while (t[x].father) {           //遍历至根节点
            x = t[x].father;            //更新遍历爸爸
            st[x] = 1;                  //记录已走过
        }
        //遍历至x节点已走过的节点,找到最近公共祖先
        while (!st[y])y = t[y].father;
        return y;
    }
    
    int main() {
        //二叉树结点个数
        cin >> n;
        //接下来的n-1行
        for (int i = 1; i < n; i++) {
            //构建二叉树
            cin >> x >> y;
            //记录关系
            a[i] = {x, y};
        }
        //通过递归建树,构建二叉树
        t[1].depth = 1, t[1].id = 1, t[1].father = 0, st[1] = 1;//根结点1
        dfs(1);
    
        //表示求从结点u到结点v的距离
        cin >> u >> v;
    
        //二叉树的深度
        int maxDepth = 0;
        for (int i = 1; i <= n; i++) maxDepth = max(maxDepth, t[i].depth);
        cout << maxDepth << endl;
    
        //二叉树的宽度
        int maxWidth = 0;
        for (int i = 1; i <= n; i++) bucket[t[i].depth]++;
        for (int i = 1; i <= n; i++) maxWidth = max(maxWidth, bucket[i]);
        cout << maxWidth << endl;
    
        //结点u到结点v间距离:结点间距离的定义:由结点向根方向(上行方向)时的边数×2,与由根向叶结点方向(下行方向)时的边数之和。
        //这里有一个最近公共祖先的问题,才能拿到正确答案
        memset(st, 0, sizeof st);
        int r = lca(u, v);
        //按题意输出
        cout << (t[u].depth - t[r].depth) * 2 + (t[v].depth - t[r].depth) << endl;
        return 0;
    }
    
  • 相关阅读:
    解决 Mac launchpad 启动台 Gitter 图标无法删除的问题
    React 与 React-Native 使用同一个 meteor 后台
    解决 React-Native mac 运行报错 error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65. To debug build logs further, consider building your app with Xcode.app, by ope
    一行命令更新所有 npm 依赖包
    swift学习笔记
    IOS语言总结
    focusSNS学习笔记
    别小看锤子,老罗真的很认真
    windowsphone开发页面跳转到另一个dll中的页面
    【令人振奋】【转】微软潘正磊谈DevOps、Visual Studio 2013新功能、.NET未来
  • 原文地址:https://www.cnblogs.com/littlehb/p/15103532.html
Copyright © 2011-2022 走看看