UVa 10410 树重建

Tree Reconstruction

Problem Description

You have just finished a compiler design homework question where you had to find the parse tree of an expression. Unfortunately you left your assignment in the library, but luckily your friend picked it up for you. Instead of e-mailing you the parse tree so that you can rewrite the solution, your friend decides to play a practical joke and sends you just the DFS and BFS trace. Rather than try to redo the entire question you decide to reconstruct the tree.

思路分析

首先这不是一道普通的根据BFS和DFS建树,因此我们需要分析这种树的遍历的性质。普通性质无需多说,这里只说必要的。

对于BFS:

同层的节点BFS编号顺序在DFS中也是按顺序出现的。根据这个我们可以一次遍历算出节点 的深度。

对于DFS:

一个节点的子孙肯定在他后面,在他同层相邻节点的前面。

对于一个节点的子孙,如果他们有的深度恰好等于这个节点的深度+1,那么这些子孙肯定是这个节点的儿子。

有了这些性质我们就可以递归建树了。

代码展现

    #include<cstdlib>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<iostream>
    #include<string>
    #include<vector>
    #include<list>
    #include<deque>
    #include<stack>
    #include<queue>
    #include<map>
    #include<set>
    #include<algorithm>
    //#pragma GCC optimize(2)
    using namespace std;
    #define ll long long
    const int maxn=1010;
    const int INF=0x7fffffff;
    inline void read(int&x){
        int data=0,w=1;
        char ch=getchar();
        while(ch!=‘-‘&&!isdigit(ch))
            ch=getchar();
        if(ch==‘-‘)
            w=-1,ch=getchar();
        while(isdigit(ch))
            data=10*data+ch-‘0‘,ch=getchar();
        x=data*w;
    }
    void write(int x){
        if(x<0)
            putchar(‘-‘),x=-x;
        if(x>9)
            write(x/10);
        putchar(‘0‘+x%10);
    }
    struct vertex{
        int depth;
        int bfsrank;
        int dfsrank;
        list<int> edge;
        void clear(){
            depth=bfsrank=dfsrank=0;
            edge.clear();
        }
        void addedge(int v){
            edge.push_back(v);
        }
    }v[maxn];
    int bfsans[maxn],dfsans[maxn];
    #define root bfsans[1]
    void build(int o,int l,int r){
    //    clog<<"in o:"<<o<<" l:"<<l<<" r:"<<r<<endl;
        for(int i=l;i<=r;++i)
            if(v[dfsans[i]].depth==v[o].depth+1)
                v[o].addedge(dfsans[i]);
        if(v[o].edge.size()){
            list<int>::iterator i=v[o].edge.begin(),ed=v[o].edge.end();
            --ed;
            while(i!=ed){
                build(*(i++),v[*(--i)].dfsrank+1,v[*(++i)].dfsrank-1);
            }
            build(*ed,v[(*ed)].dfsrank+1,r);
        }
    }
    int main()
    {
    //  freopen(".in","r",stdin);
    //  freopen(".out","w",stdout);
        int n,whodep;
        while(cin>>n){
            for(int i=1;i<=n;++i)
                v[i].clear();
            for(int i=1;i<=n;++i){
                read(bfsans[i]);
                v[bfsans[i]].bfsrank=i;
            }
            for(int i=1;i<=n;++i){
                read(dfsans[i]);
                v[dfsans[i]].dfsrank=i;
            }
    /*        clog<<"dfsrank:"<<endl;
            for(int i=1;i<=n;i++)
                clog<<i<<": "<<v[i].dfsrank<<endl;*/
            v[root].depth=0;
            whodep=1;
            for(int i=2;i<=n;++i){
                if(v[bfsans[i]].dfsrank<v[bfsans[i-1]].dfsrank)
                    ++whodep;
                v[bfsans[i]].depth=whodep;
            }
    /*        clog<<"depths: "<<endl;
            for(int i=1;i<=n;++i)
                clog<<i<<": "<<v[i].depth<<endl;*/
            build(root,2,n);
            for(int i=1;i<=n;++i){
                write(i);putchar(‘:‘);putchar(‘ ‘);
                if(v[i].edge.size()){
                    list<int>::iterator j=v[i].edge.begin();
                    while(j!=v[i].edge.end()){
                        write(*j);putchar(‘ ‘);
                        ++j;
                    }
                }
                putchar(‘\n‘);
            }
        }
    //  fclose(stdin);
    //  fclose(stdout);
        return 0;
    }

认真看了的同学应该会注意到67行我的奇怪写法。为何不写成build(\*i,v[\*i].dfsrank+1,v[\*(++i)].dfsrank-1);呢?

这背后就是我写这篇博客的主要原因。上述代码是错的。

万恶之源是函数参数压栈顺序(CALLBACK),他是从右到左压栈的。这样的话上述代码问题就很严重了。我之前就是那样写的,想不到这种注意事项让我碰上了。卡了我半中午啊o(╥﹏╥)o。谁让 std::list

教训惨重......

原文地址:https://www.cnblogs.com/autoint/p/9520696.html

时间: 2024-10-30 08:16:49

UVa 10410 树重建的相关文章

UVA 10410 - Tree Reconstruction(树)

UVA 10410 - Tree Reconstruction 题目链接 题意:给定一个树的dfs序列和bfs序列,求出这颗树 思路:拿dfs的序列,分成若干段,每一段相当一个子树,这样就可以利用bfs的序列去将dfs的序列分段,然后利用一个队列去存放每一段,不断求出子树即可.一开始以为parse tree一定是二叉树,原来不一定啊. 代码: #include <cstdio> #include <cstring> #include <vector> #include

uva 10410 - Tree Reconstruction(栈)

题目链接:uva 10410 - Tree Reconstruction 题目大意:给定一个树的BFS和DFS,求这棵树. 解题思路:用栈维护即可.对应BFS序列映射出了每个节点和根节点的距离,遍历dfs序列,对当前节点和栈顶节点比较,如果该节点距离根节点更远,则说明该节点为栈顶节点个孩子节点,则记录后将节点放入栈中.否则弹掉栈顶元素继续比较.需要注意一点,即当元素与栈顶元素的距离值大1的时候要视为相等,因为它们属于兄弟节点 #include <cstdio> #include <cst

UVA - 10410 Tree Reconstruction (树重建)

题意:给定一个树的bfs和dfs序列,升序输出每个结点的子结点列表. 分析:因为建树不唯一,假定若bfs[u] = bfs[v] + 1,则u是v的兄弟结点,否则是孩子结点.用栈维护. #pragma comment(linker, "/STACK:102400000, 102400000") #include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #in

UVA - 10410 Tree Reconstruction (根据dfs序和bfs序恢复一颗树)

题意: 分析: 这题一开始完全没有思路, 一直没有找出规律. 参考了http://www.cnblogs.com/Wade-/p/6358859.html 和 http://www.cnblogs.com/jerryRey/p/4622927.html 在dfs序列中,相邻的两个结点u,v之间(dfs_pos(u) + 1 =  dfs_pos(v)),有父子,兄弟,其他3种关系. 父子关系:在bfs中u,v并不是相邻, bfs_pos(v) > bfs_pos(u) , 那么u为v父亲, v为

UVa 112 树求和

题意:给定一个数字,以及一个描述树的字符序列,问存不存在一条从根到某叶子结点的路径使得其和等于那个数.难点在于如何处理字符序列,因为字符间可能有空格.换行等. 思路:本来想着用scanf的(后发现scanf貌似不能做),感觉太麻烦,想看网上有没有处理比较好的,一搜全是用C++的cin流的~  还是自己用C写了一下.用的getchar().getchar()主要就是比较繁琐,需要一个一个字符比较,记得刚开始做字符串题目时比较喜欢用getchar(),有的用scanf就行的自己也喜欢用getchar

UVA - 10410 Tree Reconstruction

Description You have just finished a compiler design homework question where you had to find the parse tree of an expression. Unfortunately you left your assignment in the library, but luckily your friend picked it up for you. Instead of e-mailing yo

Uva 122 树的层次遍历 Trees on the level lrj白书 p149

是否可以把树上结点的编号,然后把二叉树存储在数组中呢?很遗憾如果结点在一条链上,那将是2^256个结点 所以需要采用动态结构 首先要读取结点,建立二叉树addnode()+read_input()承担这样的工作 然后遍历二叉树,读取结点编号输出bfs() 这道题有内存池应用的背景 附链接  http://blog.csdn.net/shawngucas/article/details/6574863 #include <cstdio> #include <cstring> #inc

uva 10410 TreeReconstruction

题意,给你一颗树的bfs序和dfs序,结点编号小的优先历遍,问你可能的一种树形: 输出每个结点的子结点. 注意到以下事实: (1)dfs序中一个结点的子树结点一定是连续的. (2)bfs,dfs序中的一个结点u的后续结点一定是u或u的后兄弟结点{v},或u和{v}的后代节点{s}. (3)如果有后兄弟结点,那么bfs序中u后面紧跟着的一定是第一后兄弟结点v1, (4)如果有后代结点,那么dfs序中u后面紧跟着的一定是第一个子结点s1. 记结点u的bfs序记为bfs(u),dfs序记为dfs(v)

UVa 11695 树的直径 Flight Planning

题意: 给出一棵树,删除一条边再添加一条边,求新树的最短的直径. 分析: 因为n比较小(n ≤ 2500),所以可以枚举删除的边,分裂成两棵树,然后有这么一个结论: 合并两棵树后得到的新树的最短直径为: 这两棵树一定是这样合并的,分别取两棵树直径的中点,然后将其连接起来.这样新树的直径才是最短的. 所以在找直径的同时还要记录下路径,方便找到中点. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm>