← 返回编程题库
coding-deepest-no-drawdown-leaf-depth中等免费版10ms未尝试

远期情景树上无中间回撤的最深叶深度

Deepest No-Drawdown Leaf on a Forward Scenario Tree

开始编码

输入是一棵以两个平行层序数组形式给出的远期情景树。parent_idx[i] 是节点 i 的父节点 index;parent_idx[0] = -1 标记根。node_pnl[i] 是从父节点进入节点 i 时实现的带符号 PnL 增量(根的 node_pnl[0] 是它自己的起始前缀值)。实现 solution(parent_idx: list[int], node_pnl: list[float]) -> int,返回最深的深度值——在该深度处至少存在一个叶节点,且其对应的根到叶路径上每一个节点(包括叶本身)的累计前缀 PnL 都 >= 0。根深度为 0,根的孩子深度为 1,依此类推。若没有任何叶节点满足这条"无中间回撤"的不变式,返回哨兵 -1

节点 i 是叶节点,当且仅当没有其它节点把 i 列为父亲。树可以是二叉的,也可以是一般树(多个节点共享同一父亲是允许的)。由于 i >= 1parent_idx[i] 总在 [0, i-1] 区间,一遍前向扫描即可由父节点已算出的值递推 prefix[i] = prefix[parent_idx[i]] + node_pnl[i]depth[i] = depth[parent_idx[i]] + 1,无需递归。

"无中间回撤"是这道题的承重约束:一旦运行前缀在某个祖先(或节点本身)处跌破 0,该祖先下的整棵子树都被剪枝——这些后代的叶节点不再计入,无论后续的 node_pnl 增量多正都不行。维护每个节点的 viable 布尔标记:viable[0] = (node_pnl[0] >= 0);对 i >= 1viable[i] = viable[parent_idx[i]] AND (prefix[parent_idx[i]] + node_pnl[i] >= 0)。答案就是 viable 叶节点上 depth[i] 的最大值。

solution(
    parent_idx = [-1, 0, 0, 1, 1, 2],
    node_pnl   = [10.0, -5.0, 3.0, -8.0, 2.0, -50.0]
)

返回 2。根(深度 0)前缀为 10.0。它的孩子是节点 1 与节点 2:节点 1 的前缀 10 + -5 = 5(viable),节点 2 的前缀 10 + 3 = 13(viable)。节点 1 的孩子:节点 3 的前缀 5 + -8 = -3跌破零——剪枝,深度 2 的叶被取消资格);节点 4 的前缀 5 + 2 = 7(viable,深度 2)。节点 2 的唯一孩子是节点 5,前缀 13 + -50 = -37(剪枝)。唯一的 viable 叶节点是节点 4(深度 2),故答案为 2。若不要求"无中间回撤",节点 3 与节点 5 也都是深度 2 的候选叶——但它们的累计前缀分别跌到了 -3-37,被该不变式否决,于是节点 4 成为唯一最深的 viable 叶。

朴素枚举所有根到叶路径在最差分支下是指数复杂度;前缀转负即剪枝的前向扫描则为 O(N) 时间、O(N) 空间,配合 N <= 4000 上限非常宽裕。

实践背景

在远期利率情景树上做"情景行走式"路径规划时,交易员通常会受到一条保证金或风险预算约束——中间累计 PnL 任何时刻不能跌破零,否则在到达终端叶之前就会被强平,整条规划路径作废。风险预算、日内回撤上限、capital-at-risk 闸门,本质上都给出同一个不变式:路径有效,当且仅当其上每一段前缀都非负。能够"走到"的最深叶节点深度,就是交易员不被中途击穿的前提下能规划的最长行走路径——它在操作上明显区别于"最深叶"(不考虑回撤剪枝)和"最大终端 PnL"(不关心深度,也不关心中间前缀的不变式)。同样的"前缀转负即剪枝"递推也出现在信用迁移树上的存活路径计数、以及多步资本预算分配的可行性检验里。

约束条件

  • 1 <= N <= 4000,其中 N == len(parent_idx) == len(node_pnl);parent_idx[0] = -1 表示根。
  • 对 i >= 1,parent_idx[i] 在 [0, i-1] 之间(层序属性:父节点必出现在子节点之前)。多个节点可以共享同一个父亲(一般树,不一定是二叉树)。
  • 每个 node_pnl[i] 为带符号 float,abs(node_pnl[i]) <= 1e6;node_pnl[0] 为根节点的起始前缀值。
  • 返回最深的叶节点深度(根深度 = 0),要求该叶节点对应的根到叶路径上每一个节点(包括叶本身)的累计前缀都 >= 0;若不存在这样的叶节点,返回哨兵 -1。
  • 输出为 Python int,按精确比对。答案是深度,不是 PnL 数值,不是路径,不是节点 index。

样例

Case 1 · statement-example: deepest viable leaf is node 4 at depth 2

输入: [[-1,0,0,1,1,2],[10,-5,3,-8,2,-50]]

期望: 2

路径 0->1->4 的前缀分别为 10/5/7(始终 >=0),节点 4 是深度 2 的 viable 叶;其它候选叶(节点 3 前缀跌至 -3,节点 5 前缀跌至 -37)都被剪枝,故答案 2。

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

Case 1 · statement-example: deepest viable leaf is node 4 at depth 2

输入: [[-1,0,0,1,1,2],[10,-5,3,-8,2,-50]]

期望: 2

路径 0->1->4 的前缀分别为 10/5/7(始终 >=0),节点 4 是深度 2 的 viable 叶;其它候选叶(节点 3 前缀跌至 -3,节点 5 前缀跌至 -37)都被剪枝,故答案 2。