DP·贪心的小包 | 豆包MarsCode AI刷题

53 阅读5分钟

贪心的小包

问题描述

小包非常喜欢吃甜点,他收到了一次性送来的NN个甜点,每个甜点都有一个对应的喜爱值。 但是这还不够!小包让小哥连续送来了MM次相同的NN个甜点,并将这些甜点首尾相接排成一排。 现在,小包面前有(N×M)(N \times M)个排成一排的甜点,小包希望从中选择一段连续的甜点,使得这段甜点的总喜爱值最大化。

注意:尽管小包喜欢甜食,但有些酣可能不合口味,导致其喜爱值为负数。小包至少要选择一个甜点来满足他对甜点的贪心。

输入参数

  • 整数(N): 表示每次送来的甜点数量。
  • 整数(M): 表示送来的次数。
  • 数组data: 长度为(N),表示每个甜点的喜爱值。

返回结果

  • 一个整数,表示在N×MN \times M个甜点中可以选择的连续甜点段的最大总喜爱值。

测试样例

样例1:

输入:N= 5 ,M = 1 ,data = [1,3,-9,2,4] 输出:6 解释:选择甜点[2,4],最大总喜爱值为6

样例2:

输入:N= 5 ,N = 3 ,data = [1, 3,-9,2,4] 输出:11 解释:排列后甜点为[1,3,-9,2,4,1,3,-9,2,4,1,3,-9,2,4]。选择[2,4,1,3,-9,2,4,1,3]这段连续甜点,最大总喜爱值为11


思路:

  • 分析题目,data表示每次送来的每个甜品的喜爱值的数组,M表示送来的次数,它一共会送来M个data数组。
  • 拼接数组:首先,所有送来的甜品喜爱值拼接成一个新数组mergeArray。
  • 求最大喜爱值:其次,求数组中的最大喜爱值。即求数组中的最大子数组之和,可以考虑用 Kadane算法。
  • 按照以上步骤依次进行即可。

1. 拼接数组

  • 分类讨论:当只送一次时(m=1),data即最后要的数组;当不止送一次时(m>1),需要将其拼接起来。

  • 可以使用System.arraycopy()将其拼接起来。

  • 本次采用了一个循环,依次将其拼接起来,因为每次都是一样的数组,所以仅需要使得 目标数组的起始位置发生改变即可,根据规律,0+data.length*i即可解决目标数组拼接的起始位置问题。

 // ①拼接数组
int [] mergeArray = new int[data.length * M];
// 当只送一次,data即最后要的数组;当M > 1时,需要将其拼接起来
if(M > 1){
    for(int i = 0;i < M;i++){
        System.arraycopy(data, 0,mergeArray, 0 + data.length * i, data.length);
    }
} else if(M == 1){
    mergeArray = data;
}
// System.out.println("mergeArray = " + Arrays.toString(mergeArray));
(对应的)Java知识点整理
  • 数组定义:int [] arr = new int[长度];
  • 数组输出

直接输出是数组的哈希码值。

可以用Array.toString(数组名)才是输出数组元素。

image.png

  • 数组拼接数组:

System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

其中,src表示源数组,srcPos表示源数组要复制的起始位置,desc表示目标数组,length表示要复制的长度。

System.arraycopy是一种浅复制。简单来说 深度复制,可以将对象的值和对象的内容复制;浅复制是指对对象引用的复制。下图是System.arraycopy的参考用法。

image.png

image.png

2. 求最大喜爱值

使用Kadane算法求解最大子数组之和。

public static int kadane(int []data){
    int max_so_far,max_sum;
    max_so_far = max_sum = data[0];
    for(int i = 1;i < data.length;i++){
        max_so_far = Math.max(data[i], max_so_far + data[i]);
        max_sum = Math.max(max_so_far,max_sum);
    }
    // System.out.println("max_sum = " + max_sum);
    return max_sum;
}

知识点整理

  • kadane算法:

Kadane算法是一种用于解决最大子数组和问题的动态规划问题。最大子数组问题,是指一个数组中找到一个连续的子数组,使得子数组的元素之和最大。

基本思想:遍历数组的每个元素,同时维护两个变量:

  1. 当前子数组(max_so_far):表示以当前元素结尾的子数组的最大和。
  2. 全局最大和(max_sum):表示已经遍历过的子数组的最大和。

遍历数组的过程中,对于每个元素,做以下操作:

  1. 将当前元素加入当前子数组中,从而扩展当前子数组
  2. 以当前元素为起点开始一个新的子数组

Kadane算法的关键在于 通过比较 当前元素 和 当前元素加上前一个元素的和 来更新当前子数组和。 具体步骤如下:

  1. 初始化 max_so_farmax_sum为数组中第一个元素的值。
  2. 从数组的第二个元素开始遍历:
    • 更新max_so_far为 当前元素 和 max_so_far + 当前元素中的较大者。
    • 更薪 max_summax_summax_so_far中的较大者。
  3. 遍历完成后,max_sum即为数组中的最大子数组和。

本算法的时间复杂度为O(n),是一种高效的解决方法。


完整代码

import java.util.Arrays;

public class Main {
    public static int solution(int N, int M, int[] data) {
        // Kadane算法?①将其拼成一个数组,② 然后求数组中的最大喜爱值
        // ①拼接数组
        int [] mergeArray = new int[data.length * M];
        if(M > 1){
            for(int i = 0;i < M;i++){
                System.arraycopy(data, 0,mergeArray, 0 + data.length * i, data.length);
            }
        } else if(M == 1){
            mergeArray = data;
        }
        // System.out.println("mergeArray = " + Arrays.toString(mergeArray));

        // ②计算喜爱值
        int res = kadane(mergeArray);
        return res;
    }
    public static int kadane(int []data){
        int max_so_far,max_sum;
        max_so_far = max_sum = data[0];
        for(int i = 1;i < data.length;i++){
            max_so_far = Math.max(data[i], max_so_far + data[i]);
            max_sum = Math.max(max_so_far,max_sum);
        }
        // System.out.println("max_sum = " + max_sum);
        return max_sum;
    }

    public static void main(String[] args) {
        System.out.println(solution(5, 1, new int[]{1, 3, -9, 2, 4}) == 6);
        System.out.println(solution(5, 3, new int[]{1, 3, -9, 2, 4}) == 11);
    }
}


感受

刷题过程中,发现之前遇到过类似的题目,联想到Kadane算法,爽爆!结合昨天班会,文杰老师提到了多刷题,累计经验积少成多,真的是太棒了,实践出真理!!!重新复习一遍之前的算法,赞!反复反复加深印象。

参考文章: