到核心因子集合的最少跳数:因子相似图上的多源 BFS
Min Hops to Core Factor Set: Multi-Source BFS on a Factor-Similarity Graph
开始编码实现 solution(n_factors: int, edges: list[list[int]], core_set: list[int]) -> list[int]。交易台的风险模型锚定在一组指定的核心因子子集上——这些是已经被现有因子归因流水线吃透的、解释力良好的轴:广义市场、规模、价值、动量等。n_factors 个节点之间的因子相似图由无向边列表 edges 给出,每条 [u, v] 表示聚类层判定因子 u 与因子 v 相似。对 0..n_factors-1 中的每个因子 v,返回它沿相似边到 core_set 中最近因子的最少跳数;若 v 与所有锚点不在同一连通分量且自身又不在核心集合内,则返回 -1。
例
solution(5, [[0,1],[1,2],[2,3],[3,4]], [0]) 返回 [0, 1, 2, 3, 4]。5 个因子构成一条相似链 0-1-2-3-4,锚点 0 是唯一的核心因子,因此每个因子到核心的跳数恰好等于其在链上的下标。换成两个锚点,solution(5, [[0,1],[1,2],[2,3],[3,4]], [0,4]) 返回 [0, 1, 2, 1, 0]——多源 BFS 从两端同时发起,中间因子 2 恰好距离任一锚点 2 跳,说明答案是两个达到半径的最小值,而不是到某个固定起点的距离。
两个契约边角值得提前钉死。solution(3, [[0,1],[1,2]], []) 返回 [-1, -1, -1]:core_set 为空意味着 BFS 没有起点,每个因子相对"锚点集合"都不可达,函数对每个因子都给出哨兵。solution(5, [[0,1],[1,2],[3,4]], [0]) 返回 [0, 1, 2, -1, -1]:因子 3 与 4 自成一个连通分量,与锚点 0 互不相通,因此即使彼此连通也保持哨兵。本身就出现在 core_set 中的因子,无论是否有边,距离都是 0。
预期解法是一次 O(n_factors + len(edges)) 的多源 BFS:用 edges 建无向邻接表,把距离向量初始化为 -1,将 core_set 里所有因子以距离 0 入 FIFO 队列,然后逐层扩展——首次访问时记录当前深度并入队。自环与重复边由标准的"首次访问即定型"守卫天然过滤掉,无需特判。对每个锚点分别跑一次单源 BFS 再逐元素取最小,结果相同但成本是 O(|core_set| * (n_factors + len(edges))),在最大用例上会超时。
实现细节由 stubs/stub.py 提供。
实践背景
因子归因邻近性是多因子交易台的日度诊断。风险模型把残差收益拆解到一组固定的核心因子轴——广义市场、规模、价值、动量、低波动、质量。当研究侧加进一个候选因子(比如盈利预期修正倾斜或者横截面流动性打分),自然的问题是:沿着交易台的因子相似图走多少跳能到最近的核心因子?距离核心一两跳的新因子,可以借现有流水线得到干净、可解释的归因;距离五跳或者位于不同连通分量的因子,则需要自己的定制风险模型和自己的基差风险资本。BFS 跳数是"为吸纳这个因子,现有模型还需要扩展多少"的最便宜的标量摘要。风险团队每晚跑一遍:随着滚动窗口的因子相关性变化挪动几条边,可能伴随模型再校准而新增或撤掉一个锚点,重新发布每因子的跳数向量。把远处的因子误判成一跳,会让未对冲的暴露悄悄混入残差;不可达哨兵 -1 正是"这个因子需要单独建桶"的信号。
约束条件
- 0 <= n_factors <= 5000;因子 id 取值 0..n_factors-1
- 0 <= len(edges) <= 5000;每条 edges[k] = [u, v] 为无向边,满足 0 <= u, v < n_factors;允许自环与重复边
- 0 <= len(core_set) <= n_factors;元素为 0..n_factors-1 范围内的因子 id;允许重复并被静默去重
- 输出为长度 n_factors 的整数列表;output[v] 是 v 到 core_set 中**任意**因子的最少跳数,若不可达则为 -1
- n_factors == 0 返回 [];core_set 为空返回 [-1] * n_factors;若 v 在 core_set 中则 output[v] = 0;使用精确比对
样例
Case 1 · statement-example: 5-node line, core at one end
输入: [5,[[0,1],[1,2],[2,3],[3,4]],[0]]
期望: [0,1,2,3,4]
5 个因子排成一条边链 0-1-2-3-4,核心集合为 {0}。每个因子到 0 的最少跳数恰好是其下标,输出 [0,1,2,3,4]。
Case 2 · visible: two cores at both ends meet in the middle
输入: [5,[[0,1],[1,2],[2,3],[3,4]],[0,4]]
期望: [0,1,2,1,0]
在同一条 5 节点链上,核心集合 {0,4} 同时从两端发起 BFS,中间因子 2 到最近核心的跳数为 2,输出 [0,1,2,1,0]。
Case 3 · visible: empty core_set returns -1 for every factor
输入: [3,[[0,1],[1,2]],[]]
期望: [-1,-1,-1]
核心集合为空时,没有任何 BFS 起点,因此每个因子均不可达,返回 [-1,-1,-1]。
Case 4 · visible: factors in a different component return -1
输入: [5,[[0,1],[1,2],[3,4]],[0]]
期望: [0,1,2,-1,-1]
{0,1,2} 连通且 0 是核心,跳数依次为 0、1、2;{3,4} 与核心不连通,返回 -1。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: 5-node line, core at one end
输入: [5,[[0,1],[1,2],[2,3],[3,4]],[0]]
期望: [0,1,2,3,4]
5 个因子排成一条边链 0-1-2-3-4,核心集合为 {0}。每个因子到 0 的最少跳数恰好是其下标,输出 [0,1,2,3,4]。
Case 2 · visible: two cores at both ends meet in the middle
输入: [5,[[0,1],[1,2],[2,3],[3,4]],[0,4]]
期望: [0,1,2,1,0]
在同一条 5 节点链上,核心集合 {0,4} 同时从两端发起 BFS,中间因子 2 到最近核心的跳数为 2,输出 [0,1,2,1,0]。
Case 3 · visible: empty core_set returns -1 for every factor
输入: [3,[[0,1],[1,2]],[]]
期望: [-1,-1,-1]
核心集合为空时,没有任何 BFS 起点,因此每个因子均不可达,返回 [-1,-1,-1]。
Case 4 · visible: factors in a different component return -1
输入: [5,[[0,1],[1,2],[3,4]],[0]]
期望: [0,1,2,-1,-1]
{0,1,2} 连通且 0 是核心,跳数依次为 0、1、2;{3,4} 与核心不连通,返回 -1。