1 条题解
-
0
题目理解
游戏中有 个房间,每个房间有一把类型为 的钥匙,还有 条通道连接房间,通过通道 需要类型为 的钥匙。
玩家从房间 开始,可以收集当前房间的钥匙(如果没收集过),或者使用已有钥匙通过通道。已收集的钥匙可以永久使用。
对于每个房间 ,定义 为从房间 出发能够到达的房间数。要求找到所有满足 最小的房间 ,并用 数组标记。
关键观察
1. 可达关系的等价性
如果房间 能到达房间 ,且房间 能到达房间 ,那么它们属于同一个连通分量。但注意:可达关系不一定是对称的。
2. 钥匙收集的特性
- 钥匙可以重复使用,永远不会丢弃
- 收集钥匙只需到达对应房间一次
- 通道是否可用取决于对应类型钥匙是否已收集
3. 问题转化
问题可以看作:对于每个起点 ,计算在动态收集钥匙的条件下,通过BFS能到达的房间数。
算法设计
1. 核心思路:动态BFS与并查集
使用一种启发式算法,不断合并可达的房间集合:
- 并查集维护:将可以互达的房间合并到同一个集合
- 钥匙驱动扩展:当某个集合收集到新的钥匙类型时,尝试通过需要该钥匙的通道合并其他集合
- 迭代收敛:反复进行上述过程,直到没有新的合并发生
2. 算法步骤
步骤 1:初始化
// 每个房间初始化为独立的连通分量 for (int i = 0; i < n; i++) { id[i] = i; // 并查集初始化 }步骤 2:BFS扩展过程
对每个未处理的连通分量进行扩展:
- 起点选择:选择一个连通分量的代表房间作为起点
- BFS模拟:
- 维护当前已收集的钥匙集合
- 维护等待队列:可以到达但需要特定钥匙的房间
- 当收集到新钥匙时,激活对应的等待队列
- 结果记录:记录该连通分量可达的房间集合和大小
步骤 3:合并与优化
- 如果某个房间在BFS中被访问,但它属于另一个连通分量,则合并这两个连通分量
- 使用标记数组避免重复访问
- 使用桶(bin)数据结构高效管理需要特定钥匙的等待房间
步骤 4:迭代处理
重复执行步骤2-3,直到所有连通分量都被正确处理且没有新的合并发生。
步骤 5:结果计算
- 统计每个连通分量的大小(即 值)
- 找到最小的大小值
- 标记所有属于最小连通分量的房间
算法正确性
1. 完备性保证
算法通过迭代处理所有连通分量,确保:
- 每个房间的可达集合都被正确计算
- 钥匙的传递和通道的使用被充分考虑
2. 合并的正确性
如果房间 能到达房间 ,且它们属于不同的连通分量,合并它们是合理的:
- 合并后,从 出发可以到达 所在连通分量的所有房间
- 反之,从 出发也能到达 所在连通分量的所有房间
3. 终止性
由于房间数量有限,每次迭代至少合并两个连通分量或完成一个分量的处理,算法必然在有限步骤内终止。
复杂度分析
时间复杂度
- 最坏情况:,当所有房间都互相可达时
- 实际性能:通过启发式合并和优化,在大多数情况下接近
- 通道处理:每条通道最多被处理一次,
空间复杂度
- 邻接表存储:
- 并查集与标记数组:
- 桶数据结构:,用于管理需要特定钥匙的等待房间
实现细节
1. 数据结构设计
vector<int> id; // 并查集 vector<bool> ok; // 连通分量是否已处理 vector<bool> vst; // 访问标记 vector<bool> getk; // 已收集的钥匙标记 vector<vector<int>> bin; // 按钥匙类型分组的等待房间2. 关键函数
- BFS扩展:模拟从起点出发的钥匙收集和房间访问过程
- 连通分量合并:当发现可达的其他连通分量时进行合并
- 迭代控制:重复执行直到没有新的合并发生
3. 优化技巧
- 延迟处理:将需要特定钥匙的房间暂存,待收集到对应钥匙时再处理
- 状态复用:合并连通分量时复用已计算的可达集合
- 增量更新:只处理新收集的钥匙和对应的通道
算法标签
- 基环树
- 广度优先搜索
- 并查集
- 启发式合并
总结
本题通过巧妙的BFS模拟和并查集合并,解决了在动态收集钥匙条件下的房间可达性问题。算法虽然在最坏情况下复杂度较高,但通过多种优化技术,在实际数据上表现良好,能够处理 的大规模数据。
- 1
信息
- ID
- 5963
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 10
- 已通过
- 1
- 上传者