PTA 深入虎穴 (正解)和树的同构

  在上一篇博客中分享了尝试用单链表修改程序,虽然在Dev上运行没有错误,但是PTA设置的测试点有几个没有通过,具体不清楚问题出现在哪里,所以现在把之前正确的程序放在这里。

7-2 深入虎穴 (30 分)

著名的王牌间谍 007 需要执行一次任务,获取敌方的机密情报。已知情报藏在一个地下迷宫里,迷宫只有一个入口,里面有很多条通路,每条路通向一扇门。每一扇门背后或者是一个房间,或者又有很多条路,同样是每条路通向一扇门…… 他的手里有一张表格,是其他间谍帮他收集到的情报,他们记下了每扇门的编号,以及这扇门背后的每一条通路所到达的门的编号。007 发现不存在两条路通向同一扇门。

内线告诉他,情报就藏在迷宫的最深处。但是这个迷宫太大了,他需要你的帮助 —— 请编程帮他找出距离入口最远的那扇门。

输入格式:

输入首先在一行中给出正整数 N(<),是门的数量。最后 N 行,第 我行(1)按以下格式描述编号为 i 的那扇门背后能通向的门:

K D[1] D[2] ... D[K]

其中 K 是通道的数量,其后是每扇门的编号。

输出格式:

在一行中输出距离入口最远的那扇门的编号。题目保证这样的结果是唯一的。

输入样例:

13
3 2 3 4
2 5 6
1 7
1 8
1 9
0
2 11 10
1 13
0
0
1 12
0
0

输出样例:

12根据题目要求和输入形式,分析数据存储结构,跟树结构相似,所以采用二叉树知识进行解题。每行序号表示门序号,然后是这个门后面通向其他门个数,再来输入通向那些门。所以这个结构就像一个节点,然后它的孩子个数,跟树结构很像。
#include<iostream>
#include<queue>
using namespace std;

typedef struct //结构体数组,一个数据域存放门数量,另外用一个指针指向存放通道门序号的数组
{
    int doors;//门的数量
    int *p; //指向后面门的编号序列
}node;

int input(node *a,int n)//读入n扇门的信息 ,并返回跟所在门序号(下标)
{
    int i,j;
    bool *vi;
    vi=new bool[n+1];//找出根结点的辅助数组

    for(i=0;i<n+1;i++)
        vi[i]=false;

    for(i=1;i<n+1;i++)
    {
        cin>>a[i].doors;
        if(a[i].doors)//门后面有通道
        {
            a[i].p=new int[a[i].doors];//申请存储门后面通道
            for(j=0;j<a[i].doors;j++)
            {
                cin>>a[i].p[j];
                vi[a[i].p[j]]=true;
            }
        }
        else //door后面没有通向其他门的通道
        {
            a[i].p=NULL;
        } 

    }
    for(i=1;i<n+1;i++)//找出根结点所在下标(起点)
    {
        if(!vi[i]) return i;
    }
}

int level(node *a,int r)//从a[r]开始对a数组进行层次遍历,并返回遍历最后一个结点的序号
{//跟二叉树层次遍历相似,找到最远的门,也就是层次遍历最后的那个叶结点
    queue<int> q;
    int f,i;
    q.push(r);

    while(!q.empty())
    {
        f=q.front();
        q.pop();

        if(a[f].doors) //t号门后面有通道门
        {
            for(i=0;i<a[f].doors;i++)
            {
                q.push(a[f].p[i]);
            }
        }
    }
    return f;//遍历到最后一个门序号,即是深度最大叶结点
}

int main()
{
    node *a;//用于存储整棵树
    int n,root;
    cin>>n;
    a=new node[n+1];
    root=input(a,n);
//    cout<<root;
    cout<<level(a,root);
    return 0;
}

7-1 树的同构 (30 分)

给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。

例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。

图1                图2

现给定两棵树,请你判断它们是否是同构的。

输入格式:

输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数N (≤),即该树的结点数(此时假设结点从0到N−1编号);随后N行,第i行对应编号第i个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。如果孩子结点为空,则在相应位置上给出“-”。给出的数据间用一个空格分隔。注意:题目保证每个结点中存储的字母是不同的。

输出格式:

如果两棵树是同构的,输出“Yes”,否则输出“No”。

输入样例1(对应图1):

8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -

输出样例1:

Yes

输入样例2(对应图2):

8
B 5 7
F - -
A 0 3
C 6 -
H - -
D - -
G 4 -
E 1 -
8
D 6 -
B 5 -
E - -
H - -
C 0 2
G - 3
F - -
A 1 4

输出样例2:

No

这道题再次用到查找树的根结点的函数,跟之前完全一样,主要实现操作在于判断树同构的算法。有以下几种情况:1、两棵树为空,则同构2、有一棵树为空,则不同构3、都不为空,但是根结点名称不同,则不同构4、都不为空,同构时的符合情况:  (1)左1=左2 且 右1=右2  (2)左1=右2 且 右1=左2第4步判断操作可以基于前面3点,用递归方式实现。

#include<iostream>
#include<queue>
using namespace std;

typedef struct
{
    char name;
    int l,r;
} node;

int BuildTree(node T[]) //建立二叉树
{
    int i,N;
    bool check[100]={false};//check数组用于查找树的根节点
    char x,y;
    cin>>N;

    if(N)//树结点个数不为0
    {
        for(i=0;i<N;i++)
        {
            cin>>T[i].name>>x>>y; 

            if(x!=‘-‘)//若结点不为空,将节点索引放入左子树结点
            {
                T[i].l=x-‘0‘;
                check[T[i].l]=true;//记录此结点索引,在check数组将该位置置为true
            }
            else
            {
                T[i].l=-1;//若结点为空,将其置为-1
            }

            if(y!=‘-‘)//同上,放入右子树
            {
                T[i].r=y-‘0‘;
                check[T[i].r]=true;
            }
            else
            {
                T[i].r=-1;
            }
        }
        for(i=0;i<N;i++)//遍历check数组,除了根结点之外,其它元素为true或-1
        {
            if(!check[i]) return i;//返回根结点下标
        }
    }
    else return -1;// 若树为空,返回 -1

}

int treetonggou(node t1[],node t2[],int x,int y)//判断两棵树是否同构
{
    if(x==-1&&y==-1) return 1;//两棵树为空,同构
    else if(x==-1||y==-1) return 0;//有一棵树为空,不同构
    else if(t1[x].name!=t2[y].name) return 0;//根结点名称不同,不同构
    else//递归判断所在根结点是否符合
    {
        return((treetonggou(t1,t2,t1[x].l,t2[y].l))&&(treetonggou(t1,t2,t1[x].r,t2[y].r))//判断两棵树此结点情况:左=左并且右=右
        ||(treetonggou(t1,t2,t1[x].l,t2[y].r))&&(treetonggou(t1,t2,t1[x].r,t2[y].l)));//左=右并且右=左
    }
}

int main()
{
    node t1[100],t2[100];
    int r1,r2;
    r1=BuildTree(t1);
    r2=BuildTree(t2);//cout<<r1<<" "<<r2;
    //cout<<treetonggou(t1,t2,r1,r2);
    if(treetonggou(t1,t2,r1,r2)) cout<<"Yes";
    else cout<<"No";
    return 0;
}

做这道题开始不是很顺利,问题出在我用递归方式不对,在第4步时候是这样的:

int treetonggou(node t1[],node t2[],int x,int y)
{
    if(x==-1&&y==-1) return 1;
    else if(x==-1||y==-1) return 0;
    else if(t1[x].name!=t2[y].name) return 0;
    else
    {
        if((treetonggou(t1,t2,t1[x].l,t2[y].l))&&(treetonggou(t1,t2,t1[x].r,t2[y].r)))
                return 1;
        if((treetonggou(t1,t2,t1[x].l,t2[y].r))&&(treetonggou(t1,t2,t1[x].r,t2[y].l)));
                return 1;
    }
}    

看是用了递归函数,之后一分析,其实没有用到前面基础进行递归,而是有自己的返回值了,没有用到递归的思想,在几个测试点没通过,因为没有考虑到各种情况,比如:

遍历结果相同,但树不同构;孩子结点相同,但根结点不同,树不同构。

之后几次修改,得到上面那个很长的return语句,最后通过。

原文地址:https://www.cnblogs.com/chenzhenhong/p/10776941.html

时间: 2024-07-30 16:20:56

PTA 深入虎穴 (正解)和树的同构的相关文章

BZOJ 3012 [Usaco2012 Dec]First! wzq脑洞hash树(正解trie树)

博客风格转化计划实验篇2 题意: 给n(n<=30000)个字符串,所有字符串长度加起来不超过300000(字符串只含有小写字母). 求解哪些字符串可以称作第一字符串. 一个字符串能被称为第一字符串的条件为存在一种字典序使它排在第一名. 方法: wzq脑洞hash树-.. 然而并没有trie树优越. 并且我脑洞的这个树好处有啥:暂且不知道. 坏处有啥:很容易被卡,自带常数. 所以为什么要这么写?只是因为我跟wjc说这题我特么一定要用带hash的东西搞过去! *目标达成√ 解析: 对于以下内容,请

HDU 4251 --- 主席树(划分树是正解)

题意:查询区间中位数 思路:模板题,相当于区间第K大的数,主席树可以水过,但划分树是正解.但还没搞明白划分树,先上模板 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <cstring> 5 #include <cstdlib> 6 #include <string> 7 #include <vector> 8 #include

PTA 树的同构(25 分)

7-1 树的同构(25 分) 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数N (≤10),即该树的结点数(此时假设结点从0到N?1编号):随后N行,第i行对应编号第i个结点,给出

【Hack!】bzoj2850 TLE &amp;&amp; 求正解

题目及题解传送门 然而今天在做另一道题时回想起了这道题,发现时间复杂度并不能证明为$O(n\log n)$或$O(n\sqrt n)$. 然后仔细一想竟然发现时间复杂度是$O(n^2)$的?! 该题题解为KD-tree,然而事实上这样分割矩形的方法是非常不科学的,于是出了一组数据卡掉了它. 原题中要求的是ax+by<c的,即在ax+by-c=0这条直线以下的所有点的权值之和.那么只要让直线穿过所有矩形即可. 然后有了右图的思路: 这样建树的中序遍历就是从左下到右上的顺序,而这里面任意两个点构成的

【百度之星2014~复赛 解题报告~正解】The Query on the Tree

声明 笔者近期意外的发现 笔者的个人站点http://tiankonguse.com/ 的非常多文章被其他站点转载.可是转载时未声明文章来源或參考自 http://tiankonguse.com/ 站点,因此.笔者加入此条声明. 郑重声明:这篇记录<[百度之星2014~复赛 解题报告~正解]The Query on the Tree>转载自 http://tiankonguse.com/的这条记录:http://tiankonguse.com/record/record.php?id=674

Bzoj 1537: [POI2005]Aut- The Bus 题解 [由暴力到正解]

1537: [POI2005]Aut- The Bus Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 387  Solved: 264[Submit][Status][Discuss] Description Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 到 m编号. 每个路口用两个数(i, j) 表示(1 <= i <= n, 1 <= j

HDU 6625 three arrays 求两个序列异或最小值的排列(一个可以推广的正解

目录 题意: 解析 原题描述 @(hdu 6625求两个序列异或最小值的排列) 题意: \(T(100)\)组,每组两个长度为\(n(100000)\)的排列,你可以将\(a[]\)和\(b[]\)随机排列,可以得到\(c[i]=a[i]\)^\(b[i]\),求字典序最小的\(c[]\). 解析 一个显然对的贪心做法: 针对本题 每次两颗字典树同时往下走,如果都有\(0\)或者\(1\)这条路径,就随便同时走\(0\;or\;1\)这条路径,否则只能一个走\(0\),一个走\(1\).这样复杂

USACO2020JAN Platinum T2 官方正解的翻译?

USACO2020JAN Platinum T2 官方正解的翻译? 感觉这道题一时半会儿讲不清楚 所以把文章翻译一遍,然后以注解的形式把讲的不太清楚的地方重新理解以下. (Benjamin Qi的分析) 以下令\(MOD=10^9+7\). 一些常数优化技巧: 将\(MOD\)定义为常量. 在加或减两个int时避免使用%运算. 对于下文中提到的举证,使用固定大小的二维数组储存(而非c++中的vector套vector). 不必遍历矩阵中等于零的项(即后文中矩阵的下对角线部分). 定义一个关于模运

SDUT 3340 数据结构实验之二叉树一:树的同构

数据结构实验之二叉树一:树的同构 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. Input 输入数据包含