天梯赛-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

思路

刚刚开始的时候,看到题意,第一想到的是用并查集里面的查,来计算每个人到老祖宗是多少辈,但是这样暴力查询tle 了,所以就想到利用路径压缩的思路,再开一个数组path[],再路径压缩的过程中,填好path[],这样就可以避免很多重复的操作

code

#include <iostream>
#include <algorithm>
using namespace std;
int father[100005];
int path[100005];
void  path1(int i)
{
    int ans=0;
    int tmp=i,r=i;
    while(father[r]!=-1&&path[r]==0){
        r=father[r];
     //   cout<<i<<endl;
        ans++;
    }
    while(path[tmp]==0)
    { 
        path[tmp]=(ans--)+path[r];
        
        tmp=father[tmp];
        if(tmp==-1)break;//要注意数组越界的情况
    }
   // cout<<i<<endl;
//return 1;

   // cout<<ans<<endl;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>father[i];
    }

    for(int i=1;i<=n;i++)
    {
       path1(i);
    }
    int ans=-4;
    for(int i=1;i<=n;i++)
    {
        ans=max(ans,path[i]);
    }
    cout<<ans+1<<endl;
    int flg=1;
    for(int i=1;i<=n;i++)
    {
        if(path[i]==ans)
            if(flg)
               {flg=0;cout<<i;}
             else cout<<" "<<i;
    }
    cout<<endl;

    return 0;
}


### 天梯赛 L2 红色警报 Java 并查集 实现与解题思路 #### 解题思路概述 天梯赛 L2-013 红色警报问题可以通过并查集(Union-Find)算法解决。该问题的核心是判断移除某个节点后,是否会对整个图的连通性产生影响。具体实现时,需要维护一个并查集结构,并在每次查询时动态更新连通分量的数量[^5]。 以下是基于并查集的解题思路: 1. **初始化并查集**:为每个节点分配一个独立的集合,初始时每个节点的父亲节点是自身。 2. **合并操作**:根据题目提供的边信息,将相连的节点合并到同一个集合中。 3. **查询操作**:对于每个询问,先记录当前图的连通分量数量,然后模拟移除指定节点的操作,重新计算连通分量数量。如果连通分量数量发生变化,则说明移除该节点会影响图的连通性;否则不影响。 #### Java 实现代码 以下是一个完整的 Java 并查集实现代码: ```java import java.util.*; public class Main { static int[] parent; // 并查集数组,存储每个节点的父亲节点 static int[] size; // 存储每个集合的大小 static int n, m, q; // 节点数、边数、询问数 public static void main(String[] args) { Scanner sc = new Scanner(System.in); n = sc.nextInt(); // 节点数 m = sc.nextInt(); // 边数 q = sc.nextInt(); // 询问数 // 初始化并查集 parent = new int[n + 1]; size = new int[n + 1]; for (int i = 1; i <= n; i++) { parent[i] = i; size[i] = 1; } // 处理边信息 for (int i = 0; i < m; i++) { int u = sc.nextInt(); int v = sc.nextInt(); union(u, v); } // 处理询问 for (int i = 0; i < q; i++) { int node = sc.nextInt(); if (isCritical(node)) { System.out.println("Yes"); // 移除该节点会影响连通性 } else { System.out.println("No"); // 移除该节点不会影响连通性 } } } // 查找根节点,并进行路径压缩 static int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } // 合并两个集合 static void union(int x, int y) { int rootX = find(x); int rootY = find(y); if (rootX != rootY) { if (size[rootX] < size[rootY]) { parent[rootX] = rootY; size[rootY] += size[rootX]; } else { parent[rootY] = rootX; size[rootX] += size[rootY]; } } } // 判断移除某个节点是否会影响连通性 static boolean isCritical(int node) { int originalCount = getComponentCount(); // 记录原始连通分量数量 // 模拟移除节点 Map<Integer, Integer> tempParent = new HashMap<>(); Map<Integer, Integer> tempSize = new HashMap<>(); for (int i = 1; i <= n; i++) { if (i != node) { tempParent.put(i, parent[i]); tempSize.put(i, size[i]); } } // 重新计算连通分量数量 int newCount = 0; Set<Integer> roots = new HashSet<>(); for (int i = 1; i <= n; i++) { if (i != node) { int root = find(tempParent.get(i), tempParent); roots.add(root); } } newCount = roots.size(); // 恢复原始状态 for (int i = 1; i <= n; i++) { parent[i] = tempParent.getOrDefault(i, i); size[i] = tempSize.getOrDefault(i, 1); } return newCount > originalCount; // 如果连通分量数量增,说明移除该节点会影响连通性 } // 获取当前连通分量数量 static int getComponentCount() { Set<Integer> roots = new HashSet<>(); for (int i = 1; i <= n; i++) { roots.add(find(i)); } return roots.size(); } // 辅助函数:查找根节点(用于临时并查集) static int find(int x, Map<Integer, Integer> tempParent) { while (tempParent.get(x) != x) { x = tempParent.get(x); } return x; } } ``` #### 代码说明 1. **并查集初始化**:`parent` 数组存储每个节点的父亲节点,`size` 数组存储每个集合的大小。 2. **路径压缩与按秩合并**:通过 `find` 和 `union` 函数实现高效的并查集操作。 3. **模拟移除节点**:通过临时存储并查集状态,模拟移除指定节点后的连通性变化。 4. **判断连通性变化**:比较移除节点前后的连通分量数量,决定是否输出 "Yes" 或 "No"。 #### 时间复杂度分析 - 并查集的单次操作时间复杂度接近 O(α(n)),其中 α 是阿克曼函数的反函数,增长极慢。 - 模拟移除节点的时间复杂度为 O(n),因此总时间复杂度为 O(q * n)--- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值