深入虎穴

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

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

输入格式:

输入首先在一行中给出正整数 N(<),是门的数量。最后 N 行,第 i 行(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

  看到这道题想到的数据结构是树,但是,怎么去存储这棵树呢?第一个想到的就是二维数组,将二维数组初始化为0,根据门的编号,在对应的下标处把数值改为1。但是这存在一个问题,就是当数据量太大的时候会造成大量的空间被浪费。由二维数组中的大多数是0,少部分是1,想到了稀疏矩阵,这道题
可以通过稀疏矩阵十字链表来实现,打包一个一维数组,一栏为通道数,一栏为指针。判断通道数是否为0,如果不是,则遍历链表。再将此方法进行优化,则优化成指针后带数组


typedef struct
{
    int doors;  //通道的数量 
    int *p;  //指向通道的编号序列的指针 
}node; 
结构体数组

    先从主函数开始,首先定义一个结构体数组,用于存储从题目中得出的树,根据题目,首先输入的是门的数量。由于数据量比较大,因此需要动态申请结构体数组的空间。构造一个调用函数input(),用于输入数据。由于这道题需要用到层次遍历,因此在input()这个函数中需要返回根结点的值。

int main()
{
    node *a;  //用于存储整棵树 
    int root;
    int n;  //代表门的数量 
    cin>>n; 
    a=new node[n+1];  //需要多少开多少空间
    root=input(a,n);
    cout<<level(a,root)<<endl;
    return 0;
}
主函数

  在写input()函数时,需要定义一个布尔类型的数组,初始化为false,在通道数不为零的情况下,输入会经过的门的编号,这些门的编号一定不会是根结点,最后找出根结点,返回根结点的编号。

int input(node *a,int n)
{//读入n扇门的信息给a数组,同时返回根所在的门牌号(下标) 
    int i,j;
    bool *vi;
    vi=new bool[n+1];  //后面必须要初始化,因为没有输入 
    for(i=1;i<=n;i++) 
    {
        vi[i]=false;  //初始化vi数组的全部元素为false 
    }//for 
    for(i=1;i<=n;i++) 
    {
        cin>>a[i].doors;
        if(a[i].doors!=0)  //当通道数不为零时 
        {
            a[i].p=new int[a[i].doors];  //为a[i].p申请空间 
            for(j=1;j<=a[i].doors;j++)
            {//输入后面的通道编号 
                cin>>a[i].p[j-1];
                vi[a[i].p[j-1]]=true;  //会经过的门牌号一定不是根结点 
            }//for
        }//if
        else 
        {//doors为零的情况 
            a[i].p=NULL; 
        }
    } //for 
    for(i=1;i<=n;i++)   
    {//找出根结点,并返回根结点的下标 
            if(!vi[i])
            return i;
    }
}//input
input

  最后对整棵树进行层次遍历,返回遍历最后的一个结点的编号。

int level(node *a,int r)
{//从a[r]开始对a数组进行层次遍历,并返回遍历的最后一个结点的编号 
    queue<int> q;
    int t;
    q.push(r);
    while(!q.empty())
    {
        t=q.front();
         q.pop();
         if(a[t].doors!=0)
         {//t号门后面还有通道,后面的通道入队 
             for(int i=0;i<a[t].doors;i++)
             {
                 q.push(a[t].p[i]);
            }//for
        }//if
    }//while
    return t;
}//level
level

#include <iostream>
#include <queue>
using namespace std;
typedef struct
{
    int doors;  //通道的数量 
    int *p;  //指向通道的编号序列的指针 
}node; 

int input(node *a,int n);
int level(node *a,int r);

int main()
{
    node *a;  //用于存储整棵树 
    int root;
    int n;  //代表门的数量 
    cin>>n; 
    a=new node[n+1];  //需要多少开多少空间
    root=input(a,n);
    cout<<level(a,root)<<endl;
    return 0;
}

int input(node *a,int n)
{//读入n扇门的信息给a数组,同时返回根所在的门牌号(下标) 
    int i,j;
    bool *vi;
    vi=new bool[n+1];  //后面必须要初始化,因为没有输入 
    for(i=1;i<=n;i++) 
    {
        vi[i]=false;  //初始化vi数组的全部元素为false 
    }//for 
    for(i=1;i<=n;i++) 
    {
        cin>>a[i].doors;
        if(a[i].doors!=0)  //当通道数不为零时 
        {
            a[i].p=new int[a[i].doors];  //为a[i].p申请空间 
            for(j=1;j<=a[i].doors;j++)
            {//输入后面的通道编号 
                cin>>a[i].p[j-1];
                vi[a[i].p[j-1]]=true;  //会经过的门牌号一定不是根结点 
            }//for
        }//if
        else 
        {//doors为零的情况 
            a[i].p=NULL; 
        }
    } //for 
    for(i=1;i<=n;i++)   
    {//找出根结点,并返回根结点的下标 
            if(!vi[i])
            return i;
    }
}//input

int level(node *a,int r)
{//从a[r]开始对a数组进行层次遍历,并返回遍历的最后一个结点的编号 
    queue<int> q;
    int t;
    q.push(r);
    while(!q.empty())
    {
        t=q.front();
         q.pop();
         if(a[t].doors!=0)
         {//t号门后面还有通道,后面的通道入队 
             for(int i=0;i<a[t].doors;i++)
             {
                 q.push(a[t].p[i]);
            }//for
        }//if
    }//while
    return t;
}//level
总代码

  这道题学到了三种表示一棵树的方法,二维数组是最直接简单的,但是要考虑到数据量的问题,而且在数据量大的情况下采用二维数组会导致大量空间被浪费。由于二维数组中都是0和1,且0比1多,因此采用稀疏矩阵的方法。这道题不需要考虑到双亲,因此只需要开辟空间存放指向孩子编号的指针。

  这道题看上去比上一次的AI代码要难很多,但实际上把结构弄清楚,这道题需要解决的问题比上一次的AI代码要少,也比上一次的简单。

  本章作业完成的作业中,leave list中,一直输出的结果不对,和别人对比一下代码也没有什么多大的区别,后来找了很久才发现是把y输入成了x。这种错误真的要少犯!!而树的同构,在处理左右子树还不是很理解,要好好地去理解一下。

  接下来的目标:小测考好点;在学习新的一章时也要考虑是否需要使用前面学习的结构。不要学了一章忘一章。

猜你喜欢

转载自www.cnblogs.com/ChrisMua/p/10808489.html
今日推荐