案例075 补贴时段的抢点

案例075 补贴时段的抢点

摘要:平台午高峰每单加 1.5 元、晚高峰 2.0 元、夜宵 2.5 元,三段单均收益因此拉开到 4.2、6.9、7.4 元,单均占用 1.2、1.1、1.0 小时。把三段折成单位小时收益 3.50、6.27、7.40 元/h,本题就变成一句话:先打满最高的那段,再看上限。夜宵段 7.40 元/h 最高但有 9 单上限,线性规划给出 \(n_3=9\)、\(n_2=0.909\)、总收益 72.87 元;小时约束的影子价 6.27 元/h,夜宵上限的影子价 1.13 元/单。单量取整后掉到 66.60 元(少 6.27 元),因为剩 1 小时装不下 1.1 小时的一单。

关键词:线性规划;资源分配;影子价格;临界补贴;整数化损失

1 问题提出

1.1 三段补贴摆在面前

午高峰(11:00—13:30)、晚高峰(17:30—20:00)、夜宵(22:00—01:00)三段各有补贴,越晚补得越多:1.5、2.0、2.5 元/单。夜宵补贴最高,但夜宵的单量本来就少——全城只有那么点夜宵单,跑得再快也接不到第 10 单,所以上限写 9。三段的单均占用也不一样:午高峰堵、晚高峰更堵,夜宵路空,1.2 / 1.1 / 1.0 小时一单。

把补贴和超距加价折进收益,三段是 4.2 / 6.9 / 7.4 元一单。直觉会直接选夜宵——单价最高。但真正决定收益的是每小时赚多少:3.50 / 6.27 / 7.40 元/h。夜宵仍然第一,可它只有 9 单的容量,9 小时就把上限吃满,剩下的 1 小时必须换段。

窗口的算法要说清:题面给的 6 小时是不熬夜宵的两段窗口(午 3 + 晚 3);既然夜宵的 9 单上限要真正起作用,窗口就得延到 10 小时(白天 7 + 夜宵 3)。两个口径都留在图 5、图 6 的坐标轴里:6 小时那一端,夜宵只跑得动 6 单,上限永远闲置。

表 1 三段的补贴、单价、耗时与容量

| 时段 | 补贴/元 | 均单收益/元 | 单均耗时/h | 单位小时收益/(元/h) | 单量上限/单 | |---|---|---|---|---|---| | 午高峰 | 1.5 | 4.2 | 1.2 | 3.50 | 18 | | 晚高峰 | 2.0 | 6.9 | 1.1 | 6.27 | 22 | | 夜宵 | 2.5 | 7.4 | 1.0 | 7.40 | 9 |

排序一目了然:夜宵 7.40 > 晚高峰 6.27 > 午高峰 3.50。若没有上限,10 小时全砸夜宵就是 74.00 元;有了 9 单上限,天花板被压到 72.87 元。

1.2 三个必须回答的问题

第一,打满之后还剩多少时间、投给谁——剩 1 小时,整单不成立,但按小时计价它变成 0.909 单晚高峰(每小时 6.27 元,压过午高峰的 3.50 元);第二,上限值多少钱——多给一单夜宵容量,能多赚 1.13 元,这才是该去跟平台争的量;第三,补贴降到多少就该撤——夜宵的单位小时收益跌到 6.27 元/h(即补贴 1.37 元)时,晚高峰接班。

1.3 Gurobi 怎么解

3 个连续变量、4 条约束,单纯形一步收敛,耗时不到 0.01 秒。全案求解 163 个 LP,共用 1.7 秒——这类题的价值不在算力,而在读得懂影子价。

2 数学模型

设 \(n_1,n_2,n_3\ge 0\) 为投在午高峰、晚高峰、夜宵的单量(线性规划允许小数,含义是“把这一小时切给某段的一部分”),\(p_i\) 为均单收益,\(t_i\) 为单均占用小时,\(\bar n_i\) 为单量上限,\(T=10\) 为三段合计可用窗口。

2.2 目标函数

\[\max\; Z = 4.2\,n_1 + 6.9\,n_2 + 7.4\,n_3 \tag{1} \]

系数已经含补贴与超距加价:\(4.2=2.7+1.5\)、\(6.9=4.9+2.0\)、\(7.4=4.9+2.5\)。写目标时不要漏掉第二段的基数——晚高峰不含补贴的毛收益本来就比午高峰高 2.2 元,这是“晚高峰更远、单价更高”的体现。

2.3 约束条件

\[1.2\,n_1 + 1.1\,n_2 + 1.0\,n_3 \le 10 \tag{2} \]

\[n_1 \le 18,\qquad n_2 \le 22,\qquad n_3 \le 9 \tag{3} \]

式 (2) 是唯一的时间账:一小时只能记一次账,三段互斥地抢同一批小时。式 (3) 是容量:午晚两段的上限(18、22 单)在本窗口内根本摸不到——18 单要 21.6 小时、22 单要 24.2 小时,都远超 10 小时;只有夜宵的 9 单(正好 9 小时)会在可行域里真的卡住。

2.4 把 LP 读成“排队”

式 (1)(2) 是典型的一维装箱:每小时只装得下一段。按单位小时收益从高到低排队,贪心装填就是最优解——这是 LP 的基本性质,不需要 Gurobi 也能算。但贪心必须先看容量:夜宵装 9 单用掉 9 小时,剩 1 小时,晚高峰每小时值 6.27 元,于是余量全给晚高峰,得 \(n_2=(10-9)/1.1=0.9091\)。

3 Gurobi 求解实现

3.1 建模要点

n = m.addVars(3, lb=0.0, vtype=GRB.CONTINUOUS, name='n')
hc = m.addConstr(1.2*n[0] + 1.1*n[1] + 1.0*n[2] <= 10, name='hours')
cc = [m.addConstr(n[i] <= cap[i]) for i in range(3)]
m.setObjective(4.2*n[0] + 6.9*n[1] + 7.4*n[2], GRB.MAXIMIZE)

两个坑。其一,默认下界是 0 还是 \(-\infty\):addVars 不写 lb=0 时 Gurobi 默认 0,但若换了写法(如 addVar 逐个加)忘了下界,模型会给出负单量的荒谬解。其二,影子价要给约束起名再取 Pi,否则分不清哪个是小时、哪个是容量。

表 2 LP、整数解与三种“全投一段”的对照

| 方案 | 午高峰/单 | 晚高峰/单 | 夜宵/单 | 用时/h | 总收益/元 | |---|---|---|---|---|---| | 全投午高峰 | 8.33 | 0 | 0 | 10.0 | 35.00 | | 全投晚高峰 | 0 | 9.09 | 0 | 10.0 | 62.73 | | 全投夜宵 | 0 | 0 | 9 | 9.0 | 66.60 | | LP 最优 | 0.000 | 0.909 | 9 | 10.00 | 72.87 | | 整数最优 | 0 | 0 | 9 | 9.0 | 66.60 |

全投夜宵已经拿到 66.60 元(9 小时、9 单),LP 再把剩下的 1 小时按 6.27 元/h 卖给晚高峰,多出 6.27 元;整数解做不到这一步,因为 1 小时 < 1.1 小时,只能整单整单地跑。

4 结果分析

4.1 结构:谁的单位小时收益最高

图 1 三段的单位小时收益:夜宵 7.40 元/h 最高,但容量只有 9 单

图 1 把三段折成同一把尺子。午高峰 3.50 元/h 垫底——单均只有 4.2 元,却要耗 1.2 小时;夜宵 7.40 元/h 领先 1.13 元。绿色虚线(6.27 元/h)是本模型真正的小时价格,它等于次高段而不是最高段——因为最高段已经打满。**这两条线的落差 1.13 元,就是“上限”的代价**。

图 2 三段的上限与均单收益(气泡=单位小时收益)

图 2 用横轴把容量摆开:午高峰 18 单、晚高峰 22 单,看着富裕,实际都用不完;夜宵只有 9 单,气泡最大(效率最高)也最先见底。线性规划的点(星号)正落在夜宵的右上角——**顶到上限**,这是本题唯一真正起作用的容量约束。

图 3 全投一段、LP 最优与整数最优的五方案对比

图 3 的顺序是本题的全部结论:LP 72.87 最高,全投夜宵与整数解并列 66.60,再往下是全投晚高峰 62.73、全投午高峰 35.00。全投夜宵输在“剩 1 小时闲着”,整数解输在“1 小时装不下 1.1 小时的一单”,两者相差 6.27 元——**正好是 0.909 单晚高峰的价钱**,即 LP 拿小数单换来的那点钱。

图 4 10 h 窗口内的三段排布:夜宵 9 h 打满,尾巴 1 h 给晚高峰

图 4 是这张时间表的实物:夜宵从 0 排到 9 小时(9 单,顶满上限),晚高峰接在最后 1.0 小时。这个顺序不能倒——先排效率最高的段,再拿余量填缝;反过来先排晚高峰,夜宵的 9 单就挤不进窗口,只能干 1 小时,收益立刻塌回全投晚高峰的水平。

4.2 影子价:多一小时、多一单各值多少

前沿(图 8,见 4.4 末)的斜率就是答案。每往右一格(多投 1 单夜宵、少投 \(1/1.1=0.909\) 单晚高峰),总收益涨 1.13 元——即 \(7.4-6.9/1.1=1.1273\),这正是夜宵容量约束的影子价。右侧灰区是 \(n_3>9\) 的不可行区,前沿到此为止。

\[\lambda_{\text{cap}} = p_3 - \frac{p_2}{t_2} = 7.4 - \frac{6.9}{1.1} = 1.1273\;\text{元/单} \tag{4} \]

式 (4) 有个直白读法:多一单夜宵要用 1 小时,这 1 小时本来能给晚高峰干 \(1/1.1=0.909\) 单、赚 6.27 元,所以净增只有 1.13 元。同理,小时约束的影子价是 \(\lambda_{\text{time}}=6.2727\) 元/h——再多 1 小时,投给晚高峰(因为夜宵已满),不是 7.4 元。“若多给 1 小时该投哪段”的答案是:晚高峰,而不是继续夜宵。

结论要配套说清:只有当 \(n_3<9\) 时,多 1 小时的边际值才是 7.40 元;一旦 \(n_3\) 顶到 9,边际值立刻掉到 6.27 元,跌幅 1.13 元。这就是“打满最高段”这句话的完整版:先打到上限,之后一切按次高段定价。

4.3 整数化:小数单量买到的是“可分割时间”

把 \(n_i\) 改成整数,最优解从 \((0,0.909,9)\) 变成 \((0,0,9)\),收益从 72.87 掉到 66.60,LP 松弛高估 6.27 元(9.4%)。原因是那 1 小时余量:LP 允许它变成 0.909 单晚高峰,现实里只能整单,而 1.1 小时的单装不进 1 小时的格子。

实务含义是:LP 解的小数部分是“排班的毛边”。若能把窗口从 10 小时拉到 11 小时,整数解就能再吃一单晚高峰(6.9 元),反超当前的整数最优;若窗口只有 9 小时,整数与 LP 完全重合。所以整数化损失不是模型缺陷,而是时间粒度的度量。

4.4 灵敏度:补贴、窗口与体力

图 5 夜宵补贴 × 最优收益(右轴:夜宵单量)

图 5 只在 $p_3^{\ast}=1.37$ 元处拐一次。补贴高于它,夜宵 9 单顶满;低于它,夜宵单位小时收益跌破 6.27 元/h,整段让给晚高峰,收益从 72.87 掉到 62.73 元,一夜少 10.15 元。临界条件就是「夜宵小时收益 = 晚高峰小时收益」:

\[4.9 + s_3^{\ast} = \frac{6.9}{1.1} = 6.2727 \;\Rightarrow\; s_3^{\ast} = 1.37\;\text{元} \tag{5} \]

本案 2.5 元离临界只差 1.13 元,缓冲不到一半——这是本模型最脆的一处。平台把夜宵补贴从 2.5 调到 2.2 仍安全(\(p_3=7.1>6.27\)),要跌到 1.37 元以下,整张表才重排。

图 6 可用窗口 × 夜宵上限 的最优夜宵单量

图 6 是二维地图:横轴窗口、纵轴夜宵容量。深色区是“上限先卡死、夜宵吃满”的地带,它沿对角线扩张——只要 $T\ge \bar n_3$(1 单 1 小时),夜宵必然打满;窗口小于上限时(左下角),卡的是时间而不是容量,格子里的数就等于 $T$。本案 $(10,9)$ 落在深色区右缘,说明**上限刚好等于窗口的九成**,是最“紧”的配置。

图 7 夜宵超时率 × 最优收益(第三维,翻转点 15.2%)

图 7 把“体力”折进系数:夜宵单价高但疲劳,超时率 $q$ 让有效系数变成 $7.4(1-q)$。当 $q>15.2\%$,夜宵 9 单的收入 66.6(1-q) 跌破全投晚高峰的 62.73 元,结论**翻转**。临界式:

\[9 \times 7.4(1-q^{\ast}) + \frac{1}{1.1}\times 6.9 = \frac{10}{1.1}\times 6.9 \;\Rightarrow\; q^{\ast} = 15.2\% \tag{6} \]

斜率也很说明问题:\(q\) 每涨 1 个百分点,夜宵段少赚 0.67 元;而晚高峰那条水平线一分不动。高单价段的脆弱性正比于单价,夜宵赚得最多,也最怕跑到凌晨三点手抖。

图 8 夜宵单量—总收益前沿与不可行阴影

图 8 把 4.2 节的影子价画成斜率:前沿从 $(0,62.73)$ 一路爬到 $(9,72.87)$,每格涨 1.13 元,正是 $\lambda_{\text{cap}}=1.13$ 元/单;再往右是灰色的不可行区——上限之外的单量再多也接不到。**容量约束把一条直线生生截断**,这就是本题的几何本质。

4.5 波动与贡献

图 9 夜宵单量波动下的一夜实收分布(4000 次模拟)

图 9 承认夜宵单量本身随机(按 $\lambda=9$ 泊松抽样、截断在 9 单):均值 66.01 元,P10—P90 只有 [64.60, 66.60],全距不到 4 元。原因是每少接一单夜宵,空出的 1 小时正好塞进 0.909 单晚高峰,收益只掉 0.50 元——**两段的替代性把单量风险吸收掉了**。真正的风险不是少单,而是 4.4 节那个超时率:它直接改写系数,替代段补不回来。

图 10 三方案的段收益堆叠:夜宵贡献多少、余量贡献多少

图 10 按段拆开:LP 方案里夜宵贡献 66.60 元、晚高峰贡献 6.27 元;整数方案只剩夜宵 66.60 元;全投晚高峰则整块换成 62.73 元。三根柱子的高度差,就是“打满 + 余量”与“只打满”“只跑次高”的差别。

5 结论

(1)最优解是“夜宵打满 9 单、余量给晚高峰”:\(n_3=9\)、\(n_2=0.909\),总收益 72.87 元;若不熬夜宵(只跑 6 h),最优退化到全投夜宵 6 单的 44.40 元。
(2)上限是本题唯一的“卡点”:夜宵容量影子价 1.13 元/单,多争 1 单夜宵名额等于多拿 1.13 元;午高峰 18 单、晚高峰 22 单的上限在 10 h 内根本摸不到,影子价为 0。
(3)小时影子价是 6.27 元/h,不是 7.4 元/h:最高段打满后,边际小时的定价权交给次高段。回答“多给 1 小时投哪段”,答案是晚高峰。
(4)整数化损失 6.27 元(9.4%),全部来自那 1 小时余量装不下 1.1 小时的一单。把窗口拉到 11 小时,整数解反超;缩到 9 小时,整数与 LP 重合。
(5)两条临界线:夜宵补贴跌破 1.37 元(现 2.5,余量 1.13 元)、超时率升破 15.2%,结论都会翻到晚高峰。前者是平台调价风险(缓冲 1.13 元),后者是体力风险(缓冲仅 15.2 个百分点)——这张表该每两周重算一次。

参考文献

[1] Gurobi Optimization LLC. Gurobi Optimizer Reference Manual[Z]. 2024.
[2] Kellerer H, Pferschy U, Pisinger D. Knapsack Problems[M]. Berlin: Springer, 2004.
[3] Martello S, Toth P. Knapsack Problems: Algorithms and Computer Implementations[M]. Chichester: John Wiley & Sons, 1990.
[4] 司守奎, 孙玺菁. 数学建模算法与应用[M]. 3 版. 北京: 国防工业出版社, 2021.

复现方式:python batch_075.py;数据与结果见同目录 .csv / .json,图见 figs/

posted @ 2026-11-11 18:05  小青蛙电气  阅读(2)  评论(0)    收藏  举报