【Leetcode 每日一题 - 补卡】3259. 超级饮料的最大强化能量

问题背景

来自未来的体育科学家给你两个整数数组 e n e r g y D r i n k A energyDrinkA energyDrinkA 和 e n e r g y D r i n k B energyDrinkB energyDrinkB,数组长度都等于 n n n。这两个数组分别代表 A A A、 B B B 两种不同能量饮料每小时所能提供的强化能量。

你需要每小时饮用一种能量饮料来 最大化 你的总强化能量。然而,如果从一种能量饮料切换到另一种,你需要等待一小时来梳理身体的能量体系(在那个小时里你将不会获得任何强化能量)。

返回在接下来的 n n n 小时内你能获得的 最大 总强化能量。
注意 你可以选择从饮用任意一种能量饮料开始。

数据约束

  • n = = e n e r g y D r i n k A . l e n g t h = = e n e r g y D r i n k B . l e n g t h n == energyDrinkA.length == energyDrinkB.length n==energyDrinkA.length==energyDrinkB.length
  • 3 ≤ n ≤ 1 0 5 3 \le n \le 10 ^ 5 3≤n≤105
  • 1 ≤ e n e r g y D r i n k A [ i ] , e n e r g y D r i n k B [ i ] ≤ 1 0 5 1 \le energyDrinkA[i], energyDrinkB[i] \le 10 ^ 5 1≤energyDrinkA[i],energyDrinkB[i]≤105

解题过程

题目要求从两个数组中每次选一个数累计得到最大值,如果某轮将要选择与上一次选择中不同的数组中的数,那么这一轮不能直接选择从另一个数组中选择。

考虑递归,从前往后或者从后往前枚举都可以。每一轮选取当前值的前提,是选取同一数组的上一个数或者选取不同数组的上上个数。

为了表示方便,将两个数组拼成一个二维数组 e n e r g y D r i n k energyDrink energyDrink,根据另一维度的下标确定从哪个数组中取值。

因此,状态转移方程: d f s ( i , j ) = m a x ( d f s ( i − 1 , j ) , d f s ( i − 2 , j ⊕ 1 ) ) + c [ j ] [ i ] dfs(i,j) = max(dfs(i − 1,j), dfs(i − 2,j \oplus 1)) + c[j][i] dfs(i,j)=max(dfs(i−1,j),dfs(i−2,j⊕1))+c[j][i]。

递归入口 是 m a x ( d f s ( 0 , 0 ) , d f s ( 0 , 1 ) ) max(dfs(0, 0), dfs(0, 1)) max(dfs(0,0),dfs(0,1)),表示选取两个数组中较大的第一个数。

递归边界 是 i > e n e r g y D r i n k [ 0 ] . l e n g t h − 1 i > energyDrink[0].length - 1 i>energyDrink[0].length−1,表示已经选择完数组中的数。

递归的做法实现完成之后,可以把它等效地翻译成递推。

答案为 m a x ( d p [ n + 1 ] [ 0 ] , d p [ n + 1 ] [ 1 ] ) max(dp[n + 1][0], dp[n + 1][1]) max(dp[n+1][0],dp[n+1][1]),表示枚举最后一个数之后产生的最终结果。

初始值为 d p [ 0 ] [ j ] = d p [ 1 ] [ j ] = 0 dp[0][j]=dp[1][j]=0 dp[0][j]=dp[1][j]=0,表示尚未枚举任何数时,状态转移数组中初始为空。

具体实现

递归

java 复制代码
class Solution {
    public long maxEnergyBoost(int[] energyDrinkA, int[] energyDrinkB) {
        // 用原来的两个数组定义二维数组,注意一下这个写法
        int[][] energyDrink = {energyDrinkA, energyDrinkB};
        // 定义同等规模的 memo 数组用于记忆化搜索,防止炸内存
        long[][] memo = new long[energyDrinkA.length][2];
        // 递归入口, 也是答案
        return Math.max(dfs(0, 0, energyDrink, memo), dfs(0, 1, energyDrink, memo));
    }

    private long dfs(int i, int j, int[][] energyDrink, long[][] memo) {
        // 递归边界,数组下标越界范围 0
        if(i > energyDrink[0].length - 1) {
            return 0;
        }
        // 已经存储过的答案直接返回
        if(memo[i][j] > 0) {
            return memo[i][j];
        }
        // 求当前轮次的最大值并存储,注意新定义的二维数组形状,用 energyDrink[j][i] 来取值
        return memo[i][j] = Math.max(dfs(i + 1, j, energyDrink, memo), dfs(i + 2, j ^ 1, energyDrink, memo)) + energyDrink[j][i];
    }
}

递推

java 复制代码
class Solution {
    public long maxEnergyBoost(int[] energyDrinkA, int[] energyDrinkB) {
        int n = energyDrinkA.length;
        // 定义状态转移数组 dp,由于状态可能和上上个数有关,数组规模需要相应地扩大
        long[][] dp = new long[n + 2][2];
        for(int i = 0; i < n; i++) {
            dp[i + 2][0] = Math.max(dp[i + 1][0], dp[i][1]) + energyDrinkA[i];
            dp[i + 2][1] = Math.max(dp[i + 1][1], dp[i][0]) + energyDrinkB[i];
        }
        return Math.max(dp[n + 1][0], dp[n + 1][1]);
    }
}

梳理总结

二进制状态转换

如果一个状态变量 s t a t u s status status非零即一,那么有两种方法将它转换成另一种状态:

  • s t a t u s = 1 − s t a t u s status = 1 - status status=1−status
  • s t a t u s = s t a t u s ⊕ 1 status = status \oplus 1 status=status⊕1
相关推荐
wenyue11211 分钟前
以有效安全的方式在正式生产环境中模拟真实流量进行全站点性能测试
后端·算法·架构·压力测试
YueiL9 分钟前
数据结构每日一题|判断链表是否有环型结构
javascript·数据结构·链表
请你打开电视看看34 分钟前
创建型模式-建造者模式
java·算法·建造者模式
浪前1 小时前
排序算法之选择排序篇
java·算法·排序算法
ኈ ቼ ዽ1 小时前
python简单算法
开发语言·python·算法
Jam-Young1 小时前
Python的排序算法
python·算法·排序算法
fighting!8992 小时前
数据结构之数组与链表的差异
数据结构·链表
Am心若依旧4092 小时前
[高阶数据结构四] 初始图论
开发语言·数据结构·c++·图论
羊小猪~~3 小时前
数据结构C语言描述5(图文结合)--广义表讲解与实现
c语言·开发语言·数据结构·c++·算法·链表·visual studio