分苹果问题最easy题解:最大化小明所得苹果数的策略(云智春招笔试、b站春招笔试)
题目描述
有 m 个苹果和 n 个小孩,每个小孩有一个编号,小明的编号是 k。要求分配苹果时满足以下条件:
- 每个小孩至少分到 1 个苹果。
- 相邻编号的小孩分到的苹果数目差距不能超过 1。
- 在满足上述条件的情况下,小明分到的苹果数目要尽可能多。
求小明能分到的苹果的最大值。
解题思路
我们需要在保证相邻苹果数差不超过 1 的前提下,使小明分到的苹果最多。关键思路是以小明为中心,逐层向外扩展,每次尽可能多地分配苹果,直到苹果不足为止。
为什么选择逐层扩展?
- 由于相邻苹果数差不能超过 1,小明的位置若分到最多苹果,周围小孩的苹果数只能比小明少 1 或相等。
- 为了最大化小明的苹果数,我们每次尝试给小明所在的层增加一个苹果,并扩展相邻层,确保不违反相邻差的条件。
算法流程
- 初始化:每个小孩先分 1 个苹果,保证最小值。此时已用掉 n 个苹果,剩余
m-n
个苹果用于后续分配。 - 逐层扩展:以小明为中心,每次向外扩展一层,给该层所有小孩各加 1 个苹果。
- 第 1 层:仅小明自己,加 1 个苹果。
- 第 2 层:扩展到小明左右相邻的小孩。
- 后续每层继续向外扩展,直到苹果不够分配。
- 终止条件:当剩余苹果不足以分配当前层所需时停止,此时小明的苹果数即为最大值。
代码解析
static long dfs(int n, int m, int k) {
k = k - 1; // 转为从0开始的索引
long ans = 2; // 初始层数为2,表示小明至少分到2个苹果(后续会调整)
long need = n; // 初始需要n个苹果(每个小孩1个)
int l = 0, r = 0; // 当前层的左右边界
while (true) {
if (ans == 2) {
// 第一层仅小明自己
l = k;
r = k;
} else {
// 向外扩展一层
l = Math.max(0, l - 1);
r = Math.min(n - 1, r + 1);
}
// 当前层需要增加的苹果数
need += r - l + 1;
if (need > m) {
break;
}
ans++; // 进入下一层
}
return ans - 1; // 最终层数减1为小明的苹果数
}
- 变量说明:
l
和r
表示当前层的左右边界。need
记录已分配的苹果总数。ans
表示当前层数,每增加一层,小明的苹果数加 1。
- 关键逻辑:
- 初始层(
ans=2
)仅给小明加 1 个苹果。 - 后续每层扩展左右边界,计算需要新增的苹果数。
- 当总苹果数超过
m
时终止,返回ans-1
作为小明的最大值。
- 初始层(
复杂度分析
- 时间复杂度:O(t),其中 t 为扩展的层数。每次扩展的层数所需苹果数递增,最坏情况下 t 约为 O(√m)。
- 空间复杂度:O(1),仅需常数空间。
示例分析
假设 n=5
(5个小孩),m=15
(15个苹果),小明编号 k=3
(转为索引 2)。
- 初始分配:每个小孩 1 个苹果,已用 5 个,剩余 10 个。
- 逐层扩展:
- 第 1 层(ans=2):仅索引 2 加 1 个,总苹果 6。
- 第 2 层(ans=3):扩展至索引 1-3,加 3 个,总 9。
- 第 3 层(ans=4):扩展至索引 0-4,加 5 个,总 14。
- 第 4 层(ans=5):需要加 5 个,总 19 > 15,终止。
- 结果:返回
ans-1=4
,小明最多分到 4 个苹果。
总结
通过逐层扩展的策略,我们能够高效地找到小明能分到的最大苹果数,同时确保相邻小孩的苹果数差不超过 1。该算法的时间复杂度较低,适用于较大规模的数据。
特别提醒
在两个ans解之间的苹果数都是合理的,假设刚刚好ans为x时苹果数为20,x+1时苹果数为30,那么20到30之间都是合理的,假设取25,那么可以从底部开始往上分,可以保证符合规则。