贪心的小包
问题描述
小包非常喜欢吃甜点,他收到了一次性送来的个甜点,每个甜点都有一个对应的喜爱值。 但是这还不够!小包让小哥连续送来了次相同的个甜点,并将这些甜点首尾相接排成一排。 现在,小包面前有个排成一排的甜点,小包希望从中选择一段连续的甜点,使得这段甜点的总喜爱值最大化。
注意:尽管小包喜欢甜食,但有些酣可能不合口味,导致其喜爱值为负数。小包至少要选择一个甜点来满足他对甜点的贪心。
输入参数
- 整数(N): 表示每次送来的甜点数量。
- 整数(M): 表示送来的次数。
- 数组
data: 长度为(N),表示每个甜点的喜爱值。
返回结果
- 一个整数,表示在个甜点中可以选择的连续甜点段的最大总喜爱值。
测试样例
样例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(数组名)才是输出数组元素。
-
数组拼接数组:
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
其中,src表示源数组,srcPos表示源数组要复制的起始位置,desc表示目标数组,length表示要复制的长度。
System.arraycopy是一种浅复制。简单来说 深度复制,可以将对象的值和对象的内容复制;浅复制是指对对象引用的复制。下图是System.arraycopy的参考用法。
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算法是一种用于解决最大子数组和问题的动态规划问题。最大子数组问题,是指一个数组中找到一个连续的子数组,使得子数组的元素之和最大。
基本思想:遍历数组的每个元素,同时维护两个变量:
- 当前子数组(
max_so_far):表示以当前元素结尾的子数组的最大和。 - 全局最大和(
max_sum):表示已经遍历过的子数组的最大和。
遍历数组的过程中,对于每个元素,做以下操作:
- 将当前元素加入当前子数组中,从而扩展当前子数组。
- 以当前元素为起点开始一个新的子数组。
Kadane算法的关键在于 通过比较 当前元素 和 当前元素加上前一个元素的和 来更新当前子数组和。 具体步骤如下:
- 初始化
max_so_far和max_sum为数组中第一个元素的值。 - 从数组的第二个元素开始遍历:
- 更新
max_so_far为 当前元素 和max_so_far + 当前元素中的较大者。 - 更薪
max_sum为max_sum和max_so_far中的较大者。
- 更新
- 遍历完成后,
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算法,爽爆!结合昨天班会,文杰老师提到了多刷题,累计经验积少成多,真的是太棒了,实践出真理!!!重新复习一遍之前的算法,赞!反复反复加深印象。
参考文章: