问题描述
在猫星球上,小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
题目分析
本题要求为每只猫分配粮食,并满足以下规则:
- 每只猫至少分配 1 单位粮食。
- 若某只猫的等级高于其左边的猫,则它的分配量应比左边多。
- 若某只猫的等级高于其右边的猫,则它的分配量应比右边多。
目标是使总分配量最小化。
思路
-
初始化分配:
- 每只猫至少分配 1 单位粮食。
-
从左到右遍历:
- 检查每只猫和它左边的猫的等级关系。如果当前猫的等级比左边猫高,则它的分配量比左边猫多 1。
-
从右到左遍历:
- 检查每只猫和它右边的猫的等级关系。如果当前猫的等级比右边猫高,则它的分配量需要满足右边猫的分配量约束,并取两次分配中较大的值。
-
计算总分配量:
- 遍历最终的分配数组,将所有分配量相加。
- 遍历最终的分配数组,将所有分配量相加。
具体做法
1. 初始化分配数组
-
每只猫至少分配 1 单位粮食,这是题目要求。
-
初始化一个分配数组,大小为 n,每个元素设置为 1。
List<Integer> allocations = new ArrayList<>(Collections.nCopies(n, 1)); // 初始化每只猫至少 1 单位 -
例子: 输入
catsLevels = [1, 2, 2],输出allocations = [1, 1, 1]
相关知识点
List<Integer> allocations = new ArrayList<>()创建链表可以(在括号内)初始化Collections.nCopies(n, 1):生成一个大小为 n 的列表,所有值初始化为 1。allocations是分配数组,存储每只猫的粮食分配量。
2. 从左到右遍历
-
检查每只猫与它左边的猫的等级:
- 如果当前猫的等级高于左边的猫,则它的粮食分配量应比左边猫多 1。
for(int i = 0;i < n-1;i++){ if(cats_levels.get(i) < cats_levels.get(i+1)){ allocations.set(i+1, allocations.get(i) + 1 ); } }- 从索引 i=0 开始,逐一比较
catsLevels[i]和catsLevels[i+1]。 - 如果等级递增,设置
allocations[i+1] = allocations[i] + 1。
-
例子: 输入
catsLevels = [1, 2, 2]- 第一次比较:
catsLevels[1] > catsLevels[0],更新allocations[1] = 2。 - 第二次比较:
catsLevels[2] <= catsLevels[1],无需更新。 - 输出
allocations = [1, 2, 1]
- 第一次比较:
相关知识点
- ArrayList 链表中使用
ArrayList.get(i)和ArrayList.set(i, value)获取链表的值和设置链表的值
3. 从右到左遍历
-
检查每只猫与它右边的猫的等级:
- 如果当前猫的等级高于右边的猫,则它的粮食分配量应满足右边猫的约束。
- 取当前分配量与右边猫分配量加 1 的最大值。
for(int i = n-2;i >= 0;i--){ if(cats_levels.get(i) > cats_levels.get(i+1)){ allocations.set(i, Math.max(allocations.get(i), allocations.get(i+1) + 1 )); } }- 从索引 i=n−2开始,逐一比较
catsLevels[i]和catsLevels[i+1]。 - 如果等级递减,更新
allocations[i]为较大的分配量。
-
例子: 输入
catsLevels = [1, 2, 2]- 第一次比较:
catsLevels[1] <= catsLevels[2],无需更新。 - 第二次比较:
catsLevels[0] < catsLevels[1],无需更新。 - 输出
allocations = [1, 2, 1]
- 第一次比较:
4. 计算总分配量
-
遍历分配数组,将所有分配量相加,得到总分配量。
for(int allocation:allocations){ result += allocation; } return result;- 使用一个变量
result累计allocations数组的所有值。
- 使用一个变量
-
例子: 输入
allocations = [1, 2, 1]- 累计计算:
result = 1 + 2 + 1 = 4 - 返回
result = 4
- 累计计算:
完整代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main {
public static int solution(int n, List<Integer> cats_levels) {
int result = 0;
// 初始化,allocations用于存储分配的鱼干
List<Integer> allocations = new ArrayList<>(Collections.nCopies(n, 1));
// 从左到右分配 ---->
for(int i = 0;i < n-1;i++){
if(cats_levels.get(i) < cats_levels.get(i+1)){
allocations.set(i+1, allocations.get(i) + 1 );
}
}
// 从右到左分配 <----
for(int i = n-2;i >= 0;i--){
if(cats_levels.get(i) > cats_levels.get(i+1)){
allocations.set(i, Math.max(allocations.get(i), allocations.get(i+1) + 1 ));
}
}
for(int allocation:allocations){
result += allocation;
}
return result;
}
public static void main(String[] args) {
List<Integer> catsLevels1 = new ArrayList<>();
catsLevels1.add(1);
catsLevels1.add(2);
catsLevels1.add(2);
List<Integer> catsLevels2 = new ArrayList<>();
catsLevels2.add(6);
catsLevels2.add(5);
catsLevels2.add(4);
catsLevels2.add(3);
catsLevels2.add(2);
catsLevels2.add(16);
List<Integer> catsLevels3 = new ArrayList<>();
catsLevels3.add(1);
catsLevels3.add(2);
catsLevels3.add(2);
catsLevels3.add(3);
catsLevels3.add(3);
catsLevels3.add(20);
catsLevels3.add(1);
catsLevels3.add(2);
catsLevels3.add(3);
catsLevels3.add(3);
catsLevels3.add(2);
catsLevels3.add(1);
catsLevels3.add(5);
catsLevels3.add(6);
catsLevels3.add(6);
catsLevels3.add(5);
catsLevels3.add(5);
catsLevels3.add(7);
catsLevels3.add(7);
catsLevels3.add(4);
System.out.println(solution(3, catsLevels1) == 4);
System.out.println(solution(6, catsLevels2) == 17);
System.out.println(solution(20, catsLevels3) == 35);
}
}
题目总结
本题是一个典型的动态调整分配问题,需要在满足相邻元素约束的前提下,最小化总分配量。双向遍历的思路是解决类似问题的常见方法,如分配糖果问题。通过合理使用动态数组和遍历技巧,可以在 O(n) 时间内完成问题。
知识点
-
动态调整分配:
- 通过两次遍历(从左到右、从右到左),确保每只猫的分配量满足相邻关系条件。
-
列表初始化:
- 使用
Collections.nCopies(n, value)来创建一个初始值为value的列表。
- 使用
-
ArrayList
-
是 Java 集合框架中的一个类,用于动态存储和管理元素。它是一个可以动态调整大小的数组,与普通数组不同,
ArrayList可以动态增长和缩减。 -
创建:
List<Integer> allocations = new ArrayList<>() -
创建并初始化:
List<Integer> allocations = new ArrayList<>(Collections.nCopies(n, 1));。 -
ArrayList 队列中使用
ArrayList.get(i)和ArrayList.set(i, value)来获取队列的值和设置队列的值
-
-
动态更新数组:
- 使用
Math.max保留最大约束条件,防止被覆盖。
- 使用
感受
通过双向遍历调整分配量,能够高效地完成,并且最小化总分配量。将问题拆解成简单的操作。