一句话总结: 直接算出每个请求要占几个 Block,从占用block少的元素开始一个个塞进去,塞到装不下为止,塞进去的个数就是答案。
核心思路
题面允许在后续决策中撤销先前已接受的请求,这条规则把“必须按顺序在线决策”的约束消掉了:任何一个最终合法的请求集合,都可以先把这些请求全部接受、再在必要时撤销被拒绝的那些得到;反过来,任何时刻的最终集合也只是原请求的一个子集。所以顺序不影响答案,问题等价于——从 n 个请求中选出尽可能多的请求,使它们的 Block 占用之和不超过 m。
第 i 个请求的代价是 ci=ceil(len[i]/k),每个请求的价值都是 1。价值全都相同、代价都是正数,这种情况下的最优策略就是先装最便宜的:把 ci 从小到大排序,依次尝试加入,只要当前占用加上 ci 不超过 m 就接受,第一次放不下就停止(它后面只会更大,没有放得下的可能)。
大语言模型自回归生成时,历史 token 的 Key 与 Value 会缓存在 KV Cache 里,后面每一步解码直接复用,不必重算。序列越长,KV Cache 占的显存越多,而且是线性增长。
工程上通常先把显存切成一堆等大的块(Block)集中管理,这套机制叫内存池(Memory Pool):Block 一次分配好,每个 Block 装固定条数 token 的 KV 数据。
内存池中有 m 个等大的 Block,每个 Block 能容纳 k 条 token 的 KV 数据。
现有 n 个推理请求,第 i 个请求要处理 len[i] 条 token(输入长度与生成长度之和),它占用的 Block 数为 ceil(len[i]/k)。
在接受的请求占用 Block 总数不超过 m 的前提下,使被接受的请求数量最多。
时间限制:C/C++ 1000ms,其他语言:2000ms
内存限制:C/C++ 512MB,其他语言:1024MB
第一行三个整数 m、k、n,相邻两数之间用一个空格分隔。
第二行 n 个整数,依次是 len[1],len[2],…,len[n]。
其中:
输出一个整数,表示最多可接受的请求数量。不要输出多余的空格或空行。
输入
4 8 3
20 6 11
输出
2
解释:三个请求的 Block 需求依次为 ceil(20/8)=3、ceil(6/8)=1、ceil(11/8)=2。只接第 1 个请求,它独占 3 个 Block,剩下 1 个 Block 哪个请求都放不下,只能接受 1 个;跳过它、改接第 2 与第 3 个请求,占用 1+2=3 个 Block,可以接受 2 个。三个请求全接需要 3+1+2=6 个 Block,超过上限 4,所以最多接受 2 个。
输入
6 9 4
7 22 13 10
输出
3
解释:每个 Block 装 9 条 token,四个请求的 Block 需求如下:
| 请求 | 长度 | Block 数 |
|---|---|---|
| 1 | 7 | 1 |
| 2 | 22 | 3 |
| 3 | 13 | 2 |
| 4 | 10 | 2 |
挑选需求最小的三个请求(第 1、3、4 个),占用 1+2+2=5 个 Block,不超过 6,共接受 3 个;四个全接受需要 1+3+2+2=8 个 Block,超过上限,所以最多接受 3 个。
1≤m≤100000
1≤k≤1000
1≤n≤100000
1≤len[i]≤1000000
© CodeFun2000 · 使用条款
Scan the QR code below with WeChat to sign in
First-time scan will create your account automatically
请使用微信扫描下方二维码完成注册