天梯赛训练题10

本文介绍了一种算法,用于从庞大的家族家谱中找出辈分最小的成员名单。通过深度优先搜索,算法能有效确定家族中每一代的成员,并最终输出最小辈分及其成员编号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

L2-2 小字辈 (25 point(s))

本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。

输入格式:

输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号,其中第 i 个编号对应第 i 位成员的父/母。家谱中辈分最高的老祖宗对应的父/母编号为 -1。一行中的数字间以空格分隔。

输出格式:

首先输出最小的辈分(老祖宗的辈分为 1,以下逐级递增)。然后在第二行按递增顺序输出辈分最小的成员的编号。编号间以一个空格分隔,行首尾不得有多余空格。

输入样例:

9
2 6 5 5 -1 5 6 4 7

输出样例:

4
1 9
  • 代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define maxn 100008
vector<int>allSon[maxn];
priority_queue<int,vector<int>,greater<int> > ans;
int maxdepth=0;

//深搜的终止条件:搜完所有的人
    //(可忽略终止条件)搜完就结束了,复杂度为O(n)
//深搜需要记录和更新的:
    //所有人的最大等级,以及该等级下所有的人的编号
void dfs(int fu_,int step){
    if(step==maxdepth)
        ans.push(fu_);
    else if(step>maxdepth){
        while(!ans.empty())
            ans.pop();
        ans.push(fu_);
        maxdepth=step;
    }
    int len=allSon[fu_].size();
    rep(i,0,len)
        dfs(allSon[fu_][i],step+1);
}


int main(){
    int n,xx,father;
    scanf("%d",&n);
    rep(i,0,n){
        scanf("%d",&xx);
        if(xx==-1)
            father=i+1;//祖宗的标号
        else allSon[xx].push_back(i+1);//记录所有儿子
    }

    dfs(father,1);//深搜等级最低的后代

    //输出等级最低的所有后代的编号
    cout<<maxdepth<<endl;
    bool first=true;
    while(!ans.empty()){
        if(first)
            first=false;
        else
            cout<<" ";
        cout<<ans.top();
        ans.pop();
    }
    puts("");

    return 0;
}
  • 错误点和可行思路:

1.建立父亲到儿子的单向联系。不需要建立儿子到父亲的联系(再开一个数组就行),因为用不着。

2.深搜最小辈分,一开始就设一个最小辈分的等级值maxdepth,等级值越大辈分越低,深搜过程中不断更新maxdepth。

3.如果等级正好等于maxdepth,就将这个人的编号放进优先队列。

 

  • 时间复杂度分析:

dfs:O(n),因为刚好把所有人搜完就结束

 

### 天梯赛 L2 级别的训练资料与算法解析 #### 字符串模拟 对于字符串模拟类目,通常需要仔细分析输入输出格式以及数据范围。这类问的核心在于按照给定规则逐步处理字符串中的字符或子序列[^1]。通过样例输入和输出可以验证程序逻辑是否正确。 以下是解决此类问的关键点: - **输入/输出格式**:严格遵循目描述中的格式要求。 - **数据范围**:注意边界条件测试,尤其是最大最小值的情况。 - **坑点**:可能涉及特殊情况下的错误判断,需特别留意边缘情况的处理。 ```cpp #include <iostream> #include <vector> using namespace std; int main() { string s; cin >> s; // 输入字符串 vector<char> result; for (char c : s) { if (c >= 'a' && c <= 'z') { // 小写字母转换为大写 result.push_back(c - ('a' - 'A')); } else if (c >= 'A' && c <= 'Z') { // 大写字母保持变 result.push_back(c); } else { // 非字母跳过 continue; } } for (char c : result) { cout << c; } return 0; } ``` --- #### 最优路径规划 当遇到路径优化问时,应优先考虑如何减少必要的开销。例如,在某些场景下可以选择返回途中经过更多节点来节省整体时间成本[^2]。此方法适用于动态规划或者贪心策略的应用场合。 具体实现上可采用广度优先搜索(BFS),深度优先搜索(DFS),甚至Dijkstra最短路径算法等工具完成计算过程。 --- #### 游戏状态管理 哲哲打游戏一展示了复杂的状态转移机制设计方式[^3]。该案例利用数组记录玩家当前所在位置及其历史操作轨迹,并支持多种指令执行模式切换功能。 核心代码片段如下所示: ```cpp if(x == 0){ p = v[p][y - 1]; } else if(x == 1){ cnt[y] = p; cout << p << endl; } else if(x == 2){ p = cnt[y]; } ``` 上述结构清晰表达了同命令对应的具体行为定义。 --- #### 座位分配方案 针对特定编号体系下的排列组合需求,则可以通过循环迭代生成满足约束条件的结果集合[^4]。下面给出一段示范代码用于演示这一原理的实际应用效果。 ```cpp for(int i=1;i<=m;i++){ printf("#%d\n",i); for(int j=(i-1)*n+1;j<i*n;j++)printf("%d ",j); puts(""); } ``` 以上函数实现了基于固定间隔递增规律构建目标列表的功能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值