← 返回编程题库
coding-portfolio-whole-share-knapsack中等免费版2000ms未尝试

单名集中度限制下的整股组合分配

Whole-Share Portfolio Allocator under a Single-Name Cap

开始编码

某小盘股组合经理在严格的单名集中度上限下管理整股账户:任一截面上,每只名字至多持有一股。给定整数的现金预算与候选名字清单——每只候选含整数的整股成本与持有一股至下一截面的绝对美元期望 P&L——挑选一个不超过预算且总期望 P&L 最大的子集。与连续型的均值-方差配置不同,这里的最小交易单位是整股,因此问题完全离散化。

实现 solution(budget: int, candidates: list[tuple[int, float]]) -> float。每个候选是长度 2 的序列 (cost, expected_return),第一个元素是整数成本,第二个是浮点期望回报。candidates[i] = [cost_i, expected_return_i],其中 cost_i 是买入名字 i 的一股所需整数美元,expected_return_i 是持有该股的绝对美元期望回报。组合经理可自由持有现金(现金回报为零),故返回值非负。candidates 为空或 budget 为零时均返回零值,结果类型为 Python float

举例,solution(10, [[3, 4.0], [4, 5.0], [5, 6.0]]) 返回 11.0:在该预算下最优子集是成本为四与成本为五的两只(合计九元),回报 5.0 + 6.0 = 11.0;成本为三加成本为四仅得 9.0,而三只全买将超出预算。再如 solution(5, [[2, -1.0], [3, 4.0]]) 返回 4.0——成本为二的候选预期回报为负,组合经理在该名字上选择持有现金,只买入成本为三的候选。

实践背景

整股集中度约束在真实资金管理中无处不在:小盘 / 微盘组合中一手 100 美元的整股就可能突破单名 5% 的上限;期权 overlay 桌台买保护性 put 时合约张数必须为整数;UCITS 与 40-Act 基金受单一发行人持仓比例限制;家族办公室出于合规原因要求"每只名字至多 1 股"。这些场景下,连续型的均值-方差工具让位给整数规划,而这道题的一维 0/1 背包递推 dp[w] = max(dp[w], dp[w - cost] + ret) 反向扫描——正是小 N 情形下的标准实现。当 N 增大或叠加额外约束(行业上限、贝塔中性、换手率预算)时,生产环境会切换到分支定界或商用 MIP,但"一只名字一股"这一离散结构本身——也是把问题变难的根源——正是该 DP 所捕获的。

约束条件

  • 0 ≤ `budget` ≤ 5000
  • 0 ≤ N ≤ 200,其中 `N = len(candidates)`
  • 每个候选是一个长度为 2 的列表 `[cost, expected_return]`
  • 1 ≤ `cost` ≤ 5000(整数;一股整股,不允许碎股)
  • -100.0 ≤ `expected_return` ≤ 100.0(浮点数;持有 1 股到下个截面的绝对美元 P&L)
  • 每只名字至多 1 股——单名集中度上限强制 0/1 语义
  • `candidates` 为空 → 返回 `0.0`;`budget == 0` → 返回 `0.0`
  • 若没有任何子集能放进预算,返回 `0.0`(全部持有现金)
  • 浮点比较容差:`rel_tol = 1e-9`,`abs_tol = 1e-12`

样例

Case 1 · three names: take cheap pair

输入: [10,[[3,4],[4,5],[5,6]]]

期望: 11

总预算 10,挑选成本为 3 和 4 的两个候选,合计 7 元,期望回报 4.0+5.0=9.0;若改选 4+5=9 元的两个,期望回报为 5.0+6.0=11.0,后者更优,故返回 11.0。

Case 2 · budget fits one name exactly

输入: [7,[[7,2.5],[3,1],[4,1.4]]]

期望: 2.5

预算恰好 7 元;买成本为 7 的候选回报 2.5,买成本为 3 与 4 的两只回报 1.0+1.4=2.4。比较后选前者,返回 2.5。

Case 3 · negative-return share is skipped

输入: [5,[[2,-1],[3,4]]]

期望: 4

成本为 2 的候选预期回报为负,持有现金优于持有它;只买成本 3 的候选,返回 4.0。

最近提交

还没有提交记录。

编码区

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

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

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

Case 1 · three names: take cheap pair

输入: [10,[[3,4],[4,5],[5,6]]]

期望: 11

总预算 10,挑选成本为 3 和 4 的两个候选,合计 7 元,期望回报 4.0+5.0=9.0;若改选 4+5=9 元的两个,期望回报为 5.0+6.0=11.0,后者更优,故返回 11.0。

Case 2 · budget fits one name exactly

输入: [7,[[7,2.5],[3,1],[4,1.4]]]

期望: 2.5

预算恰好 7 元;买成本为 7 的候选回报 2.5,买成本为 3 与 4 的两只回报 1.0+1.4=2.4。比较后选前者,返回 2.5。

Case 3 · negative-return share is skipped

输入: [5,[[2,-1],[3,4]]]

期望: 4

成本为 2 的候选预期回报为负,持有现金优于持有它;只买成本 3 的候选,返回 4.0。