原题目:
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-digitID
's of its children. For the sake of simplicity, let us fix the root ID to be01
.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 and02
is its only child. Hence on the root01
level, there is0
leaf node; and on the next level, there is1
leaf node. Then we should output0 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;
}
从这题学到了许多,值!