PAT真题练习(甲级)——1004Counting Leaves

原题目:

A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 0<N<100, the number of nodes in a tree, and M (<N), the number of non-leaf nodes. Then M lines follow, each in the format:

ID K ID[1] ID[2] ... ID[K]

where ID is a two-digit number representing a given non-leaf node, K is the number of its children, followed by a sequence of two-digit ID's of its children. For the sake of simplicity, let us fix the root ID to be 01.

The input ends with N being 0. That case must NOT be processed.

Output Specification:

For each test case, you are supposed to count those family members who have no child for every seniority level starting from the root. The numbers must be printed in a line, separated by a space, and there must be no extra space at the end of each line.

The sample case represents a tree with only 2 nodes, where 01 is the root and 02 is its only child. Hence on the root 01 level, there is 0 leaf node; and on the next level, there is 1 leaf node. Then we should output 0 1 in a line.

Sample Input:

2 1
01 1 02

Sample Output:

0 1


中文题目:
家庭关系可以用家谱树来表示,给定一个家谱树,你的任务是找出其中没有孩子的成员。

输入格式
第一行包含一个整数 N 表示树中结点总数以及一个整数 M 表示非叶子结点数。

接下来 M 行,每行的格式为:

ID K ID[1] ID[2] ... ID[K]

ID 是一个两位数字,表示一个非叶子结点编号,K 是一个整数,表示它的子结点数,接下来的 K 个 ID[i] 也是两位数字,表示一个子结点的编号。
为了简单起见,我们将根结点固定设为 01。
所有结点的编号即为 01,02,03,…,31,32,33,…,N。

输出格式
输出从根结点开始,自上到下,树的每一层级分别包含多少个叶子节点。

输出占一行,整数之间用空格隔开。

数据范围
0<N<100
输入样例:

2 1
01 1 02
1
2
输出样例:

0 1
1


样例解释:
该样例表示一棵只有 2 个结点的树,其中 01 结点是根,而 02 结点是其唯一的子节点。
 因此,在根这一层级上,存在 0 个叶结点;在下一个级别上,有 1 个叶结点。
所以,我们应该在一行中输出0 1。

在这里插入图片描述

   我看到题第一反应是用深度优先遍历(DFS),后来看了人家的做法广度优先也可(BFS)

先用DFS写:

#include<iostream>
#include<algorithm>//定义了一组专门设计用于元素范围的函数集合,范围是可以通过迭代器或指针访问的任何对象序列 
#include<vector>//简直就是一切皆可装,而且它的容量一般也不需要关心
/*编译器也会根据你的vector中所装元素的数量进行动态的分配容量,可以把它看作是一个加强版的数组*/ 
#include<queue>//队列的基本函数 
using namespace std;
const int N = 110;
int n, m;
vector<int>vv[N];//定义一个树结点总数 
int r_depth[N]; //定义一个树的深度 
int max_depth; //定义最大树的深度 
void dfs(int u,int depth) {  //深度优先遍历 ,结点u和深度 
	max_depth = max(max_depth, depth);  //更新最大深度
	//判断该结点是否是叶子结点
	if (vv[u].size() == 0) { //是这层的叶子结点(子节点)  size()返回vv[u]中元素的个数
		r_depth[depth]++;//每层计数叶结点总和 
		return;
	}
	for (int i = 0; i < vv[u].size(); i++) { 
		int e=vv[u][i];
        dfs(e, depth + 1);
		dfs(vv[u][i], depth + 1); //递归,深度加一 
	}
}
int main() {
	cin >> n >> m;//输入树的总结点数目n,和非叶子节点数m 
	for (int i = 0; i < m; i++) {
		int a,num;
		cin >> a >> num; //输入非叶子结点的编号ID,num表示子节点数 
		for (int j = 0; j < num; j++) {
			int b;
			cin >> b; // 输入子节点 
			vv[a].push_back(b);// 在a的最后一个向量后插入一个元素,其值为b
		}
	}
	dfs(1,0); //初始化,源点为1,深度为0 
	for (int i = 0; i <=max_depth; i++) {
		cout << r_depth[i] << " "; //输出每层的叶子结点数,如0 2 5表示第一层只有根节点,所以0个叶子结点,第二层有2个叶子结点,第三层有5个叶子结点 
	}
	cout << endl;
	return 0;
}

vector 是向量类型,它可以容纳许多类型的数据,如若干个整数,所以称其为容器。vector 是C++ STL的一个重要成员,使用它时需要包含头文件:

#include<vector>;  
    (1)a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
    (2)a.assign(4,2); //是a只含4个元素,且每个元素为2
    (3)a.back(); //返回a的最后一个元素
    (4)a.front(); //返回a的第一个元素
    (5)a[i]; //返回a的第i个元素,当且仅当a[i]存在2013-12-07
    (6)a.clear(); //清空a中的元素
    (7)a.empty(); //判断a是否为空,空则返回ture,不空则返回false
    (8)a.pop_back(); //删除a向量的最后一个元素
    (9)a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+         3(不包括它)
    (10)a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
    (11)a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
    (12)a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
    (13)a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8         ,插入元素后为1,4,5,9,2,3,4,5,9,8
    (14)a.size(); //返回a中元素的个数;
    (15)a.capacity(); //返回a在内存中总共可以容纳的元素个数
    (16)a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
    (17)a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
    (18)a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才         显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能) 
    (19)a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换
    (20)a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<

向向量a中添加元素

1  vector<int> a;
2 for(int i=0;i<10;i++)
3 a.push_back(i);

存储类型:

vector<类型>标识符
vector<类型>标识符(最大容量)
vector<类型>标识符(最大容量,初始所有值)
 
int i[5]={1,2,3,4,5}
vector<类型>vi(i,i+2);//得到i索引值为3以后的值
 
vector<vector<int>>v; 二维向量//这里最外的<>要有空格。否则在比较旧的编译器下无法通过


  vector二维数组两种定义方法:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int N=5, M=6;
    vector<vector<int>> obj(N); //定义二维动态数组大小5行
    for(int i =0; i< obj.size(); i++)//动态二维数组为5行6列,值全为0
    {
        obj[i].resize(M);
    }
 
    for(int i=0; i< obj.size(); i++)//输出二维动态数组
    {
        for(int j=0;j<obj[i].size();j++)
        {
            cout<<obj[i][j]<<" ";
        }
        cout<<"\n";
    }
    return 0;
}

下面写BFS(广度优先遍历)方法:


#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
 
int n, m;
vector<int>v[110];
int cnt[110], max_depth;//记录每层叶子结点个数和最大深度
int high[110];//记录每个节点的深度
void bfs(int u) // 广度优先搜索(列表) 
                //因为要求每层的叶子结点且不要求顺序,那就是从上到下,而深度是从下向上 
{
    queue<int>q;
    q.push(u);
    high[u]=0;
    while(q.size()){
        int top=q.front();
        q.pop();
        max_depth = max(max_depth,high[top]);
        if (v[top].size() == 0)  // 说明u是叶子节点
        {
        cnt[high[top]] ++ ;
        continue;
      }
    for (int i = 0; i<v[top].size(); i++){
        int e=v[top][i];
        high[e]=high[top]+1; // 深度加一 
        q.push(e); //e是叶子节点,将e压入堆栈 
      }
   }
}
 
 
int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i ++ )
    {
        int id, k;
        cin >> id >> k;
        while (k -- )
        {
            int son;
            cin >> son;
            v[id].push_back(son);
        }
    }
 
    bfs(1);//根节点初始化 
    cout << cnt[0];
    for (int i = 1; i <= max_depth; i ++ ) cout << ' ' << cnt[i];
    cout << endl;
 
    return 0;
}
 

从这题学到了许多,值!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值