L2-026 小字辈
原题:
本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。
输入格式:
输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号,其中第 i 个编号对应第 i 位成员的父/母。家谱中辈分最高的老祖宗对应的父/母编号为 -1。一行中的数字间以空格分隔。
输出格式:
首先输出最小的辈分(老祖宗的辈分为 1,以下逐级递增)。然后在第二行按递增顺序输出辈分最小的成员的编号。编号间以一个空格分隔,行首尾不得有多余空格。
输入样例:
9
2 6 5 5 -1 5 6 4 7
输出样例:
4
1 9
本题思路很简单,对于每一个节点,只要根据祖先信息一步一步找到已确定辈分的祖先,得到辈分数,同时在整个寻找过程中,途经的节点也同时得到了辈分数,这样在对其他节点查找辈分数时就节约了时间。思路来源并查集查找祖先函数。
#include <iostream>
#include <cstring>
using namespace std;
//par祖先数组parent,lev辈分数数组level,i当前判断辈分数节点
int help(int *par,int *lev,int i) {
if (lev[i] != 0) { //未获得辈分数的节点为0,祖先-1的辈分数已被确定为1
return lev[i]; //辈分数存在则返回
}
//节点的辈分数为父节点的辈分数加1
//整个对某一结点的查找路径中的所有节点都确定了辈分数
return lev[i] = help(par, lev, par[i]) + 1;
}
int main() {
ios_base::sync_with_stdio(0);cin.tie(0); //加速io
int n;
cin >> n;
int *par = new int[n + 1];
int *lev = new int[n + 1];
memset(lev, 0, (n + 1) * sizeof(int));
for (int i = 1; i < n + 1; i++) {
cin >> par[i];
if (par[i] == -1) {
lev[i] = 1; //祖先-1辈分数为1
}
}
int max = 0;
for (int i = 1; i < n + 1; i++) {
int tmp = help(par, lev, i);
if (tmp > max) { //找到最大辈分数
max = tmp;
}
}
cout << max << endl;
bool first = true;
for (int i = 1; i < n + 1; i++) {
if (max == lev[i]) {
if (first) first = false; //控制输出时的空格
else cout << " ";
cout << i;
}
}
return 0;
}