题目描述
在猫星球上,小R负责给一行排队的猫分发鱼干。每只猫有一个等级,等级越高的猫应该得到更多的鱼干。 规则如下:
- 每只猫至少得到一斤鱼干。
- 如果一只猫的等级高于它相邻的猫,它就应该得到比相邻的猫更多的鱼干。
小R想知道,为了公平地满足所有猫的等级差异,他至少需要准备多少斤鱼干。
测试样例
样例1:
输入:n = 3, cats_levels = [1, 2, 2]
输出:4
样例2:
输入:n = 6, cats_levels = [6, 5, 4, 3, 2, 16]
输出:17
样例3:
输入:n = 20, cats_levels = [1, 2, 2, 3, 3, 20, 1, 2, 3, 3, 2, 1, 5, 6, 6, 5, 5, 7, 7, 4]
输出:35
题目分析
此问题是典型的 分发问题,具有局部规则约束:
- 每只猫至少得到一斤鱼干。
- 等级更高的猫比其相邻等级低的猫获得更多的鱼干。
解决思路解析
贪心法
对于这道题目而言,必须得先确定好其中一边,之后再去确定另一边才行。比如说,可以先去比较每一只猫的左边情况,然后再来比较右边的情况。要是同时对两边进行考虑的话,那必然会出现顾此失彼的状况。
我们不妨先确定右边猫的等级大于左边的这种情况(具体做法就是从左向右进行遍历)。
在这种情形下,存在着局部最优的情况:只要右边猫的等级比左边猫的等级要高,那么右边的猫就能够再多获得一斤鱼干;而从全局最优的角度来看:在相邻的猫当中,等级较高的右猫所获得的鱼干数量要比左猫更多。 通过这样的局部最优情况,是能够推导出全局最优情况的。
利用两次遍历(从左向右和从右向左),在每次遍历中根据等级递增规则调整每只猫的鱼干数量:
- 从左向右遍历:确保每只猫的鱼干数量大于其左侧猫(如果它的等级更高)。
- 从右向左遍历:确保每只猫的鱼干数量大于其右侧猫(如果它的等级更高)。
- 两次遍历结束后,将每只猫的鱼干取最大值,最终得到最小满足条件的总鱼干数。
代码实现
public static int solution(int n, List<Integer> cats_levels) {
if (n == 0 || cats_levels == null || cats_levels.size() == 0) return 0;
int[] fish = new int[n];
// 每只猫至少得到1斤鱼干
for (int i = 0; i < n; i ++) {
fish[i] = 1;
}
// 从左向右遍历
for (int i = 1; i < n; i ++) {
if (cats_levels.get(i) > cats_levels.get(i - 1)) {
fish[i] = fish[i - 1] + 1;
}
}
// 从右向左遍历
for (int i = n - 2; i >= 0; i --) {
if (cats_levels.get(i) > cats_levels.get(i + 1)) {
fish[i] = Math.max(fish[i], fish[i + 1] + 1);
}
}
// 计算总和
int res = 0;
for (int f : fish) {
res += f;
}
return res;
}
时间复杂度: 需要正序和逆序各遍历一次数组,时间复杂度为 O(n)。
空间复杂度: 使用了额外的数组存储每只猫的鱼干数量,空间复杂度为 O(n)。
学习心得
贪心算法一般分为如下四步:
- 将问题分解为若干个子问题
- 找出适合的贪心策略
- 求解每一个子问题的最优解
- 将局部最优解堆叠成全局最优解
知识点延伸
- 贪心法:在满足局部最优的基础上,逐步构建全局最优解。
- 面试应用:此类题目广泛应用于资源分配问题,常见于编程面试中。
- 进一步扩展:可以研究当问题中加入更多约束(如鱼干总量限制)时的解决方案,例如使用二分查找或基于优先队列的贪心算法。