贪心 + 模拟 · 贪心猫的鱼干分配(中) | 豆包MarsCode AI刷题

68 阅读5分钟

问题描述

在猫星球上,小R负责给一行排队的猫分发鱼干。每只猫有一个等级,等级越高的猫应该得到更多的鱼干。规则如下:

  1. 每只猫至少得到一斤鱼干。
  2. 如果一只猫的等级高于它相邻的猫,它就应该得到比相邻的猫更多的鱼干。

小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 单位粮食。
  2. 若某只猫的等级高于其左边的猫,则它的分配量应比左边多。
  3. 若某只猫的等级高于其右边的猫,则它的分配量应比右边多。

目标是使总分配量最小化。


思路

  1. 初始化分配

    • 每只猫至少分配 1 单位粮食。
  2. 从左到右遍历

    • 检查每只猫和它左边的猫的等级关系。如果当前猫的等级比左边猫高,则它的分配量比左边猫多 1。
  3. 从右到左遍历

    • 检查每只猫和它右边的猫的等级关系。如果当前猫的等级比右边猫高,则它的分配量需要满足右边猫的分配量约束,并取两次分配中较大的值。
  4. 计算总分配量

    • 遍历最终的分配数组,将所有分配量相加。 0f91276af2b6dd16068007acd9493b5.jpg

具体做法

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)来获取队列的值和设置队列的值

      image.png image.png
  • 动态更新数组

    • 使用Math.max保留最大约束条件,防止被覆盖。

感受

通过双向遍历调整分配量,能够高效地完成,并且最小化总分配量。将问题拆解成简单的操作。

参考 :Java ArrayList | 菜鸟教程