zoukankan      html  css  js  c++  java
  • [CodeForces

    [CodeForces - 1225F]Tree Factory【树】【dfs】【贪心】

    标签:codeforces题解 贪心 树 dfs


    题目描述

    Time limit
    2000 ms
    Memory limit
    524288 kB
    Source
    Technocup 2020 - Elimination Round 2
    Tags
    constructive algorithms greedy trees *2500

    题面

    CodeForces - 1225F.png

    Input1

    5
    0 0 1 1

    Output1

    0 2 1 4 3
    2
    1 3

    Input2

    4
    0 1 2

    Output2

    0 1 2 3
    0

    题目大意

    将一棵竹子(链)变成一棵树的操作:可以将这棵树中的任意非根结点接到它的祖父结点(前驱的前驱)上,且这个结点的子孙全不变。

    题目中给定结点数,和除根结点外每个结点的父节点,即给定一棵树。问什么样的竹子(链)变成这棵树所花费的操作数最少。输出这棵竹子,操作数,和每次需要操作操作的结点。

    例如,
    给定一棵树
    新建 PPTX 演示文稿 _2__01.png

    生成它所需操作数最少的竹子(链)为
    IMG_20200113_163255.jpg

    需要依次对1结点和3结点操作
    新建 PPTX 演示文稿 _2__03.png


    解析

    • 题目问的是要由链生成树,其实我们首先要做的是由给定的这棵树生成操作数最少的链。
      我们先从简单的问题开始思考,给定一棵如下二叉树。根节点的左子树高度小于右子树。
      新建 PPTX 演示文稿 _2__04.png
      将2结点接到3结点的后面,生成0132456的链,操作数为2。
      新建 PPTX 演示文稿 _2__05.png
      将1结点接到6结点的后面,生成0245613的链,操作数为4。
      新建 PPTX 演示文稿 _2__06.png
      由上述的例子可见,对于一个结点,如果它的左子树高度小于右子树,需要把它的右子树全部接到左子树之后;如果它的右子树高度小于左子树,需要把它的左子树全部接到右子树之后。这个结论同样也可以推广到普通树,对于一个结点,应该按其子树高度将各个子树依次相接。

    • 建好了链,下面的问题是如何通过操作将这个链还原为树。
      (a[1 cdots n])为链的次序,(p[u])为链中结点(u)的前驱结点,(fa[u])为树中结点(u)的父亲结点。
      (a[1])开始循环到(a[n]),依次对每个结点(a[i])进行操作。如果(a[i])的链中前驱(p[a[i]])不是它树中的父亲结点(fa[a[i]]),就进行一次操作把(a[i])接到(a[i])的前驱的前驱上。并记录下a[i]。每次操作(p[a[i]])的值都改变。

      循环的次序为什么是有序的,不能反着来?
      因为反着来无法保证最优解。链(a[1 cdots n])本身就是高度小的子树在左边,高度大的子树在右边。只有按照(a[1])(a[n])(从左到右)的顺序进行操作,才能保证每个子树还原的操作数是最小的。


    通过代码

    /*
    Status
    	Accepted
    Time
    	93ms
    Memory
    	10572kB
    Length
    	1525
    Lang
    	GNU G++11 5.1.0
    Submitted
    	2020-01-13 17:20:42
    RemoteRunId
    	68700673
    */
    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAXN = 1e5 + 50;
    
    int fa[MAXN], p[MAXN], high[MAXN], a[MAXN], n, cnt = 0;
    vector<int> e[MAXN], ans;
    
    inline int read()    //1e5的数据量,使用快读.
    {
        int res = 0;
        char ch;
    
        ch = getchar();
    
        while(!isdigit(ch))
            ch = getchar();
        while(isdigit(ch)){
            res = (res << 3) + (res << 1) + ch - 48;
            ch = getchar();
        }
    
        return res;
    }
    inline bool cmp(int i, int j)
    {
        return high[i] < high[j];
    }
    void dfs1(int u)                    //第一次dfs,算出每个结点的高度.
    {
        if(!e[u].size()){
            high[u] = 1;
            return;
        }
        for(int i = 0; i < e[u].size(); i ++)
            dfs1(e[u][i]);
        int maxx = 0;
        for(int i = 0; i < e[u].size(); i ++)
            maxx = max(maxx, high[e[u][i]]);
        high[u] = maxx + 1;
        return;
    }
    void dfs2(int u)                 //第二次dfs找出链的顺序a[1..n].
    {
        a[++ cnt] = u;
    
        sort(e[u].begin(), e[u].end(), cmp);     //按照子树高度由小到大排序.
    
        for(int i = 0; i < e[u].size(); i ++)
            dfs2(e[u][i]);
        return;
    }
    
    int main()
    {
        n = read();
    
        for(int i = 1; i < n; i ++){
            int t;
    
            t = read();
    
            e[t].push_back(i);     //由父结点指向儿子的边.
            fa[i] = t;             //i的父亲是t.
        }
    
        dfs1(0);       //两次dfs找到链.
        dfs2(0);
    
        for(int i = 1; i <= n; i ++)
            printf("%d%c", a[i], i == n? '
    ': ' ');
    
        for(int i = 2; i <= n; i ++)
            p[a[i]] = a[i - 1];              //预处理p[1...n]数组.
        for(int i = 2; i <= n; i ++){        //进行还原.
            int u = a[i];
    
            while(p[u] != fa[u]){
                ans.push_back(u);           //每次操作都记录下a[i],存在ans中.
                p[u] = p[p[u]];
            }
        }
    
        printf("%d
    ", ans.size());
        for(int i = 0; i < ans.size(); i ++)
            cout << ans[i] << " " ;
    
    
        return 0;
    }
    
    

  • 相关阅读:
    What is systemvolumeinformation? delete it?
    What is "found.000" ? How to deal with it?
    install Mac OS on Vmware
    字符串数组全排列
    Hadoop开发相关问题
    String直接赋值和使用new的区别
    输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的
    括号匹配问题
    预编译语句
    两个有序单链表合并成一个有序单链表的java实现
  • 原文地址:https://www.cnblogs.com/satchelpp/p/12189533.html
Copyright © 2011-2022 走看看