【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 energyDrinkAi, energyDrinkBi \le 10 ^ 5 1≤energyDrinkAi,energyDrinkBi≤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)) + cji dfs(i,j)=max(dfs(i−1,j),dfs(i−2,j⊕1))+cji

递归入口 是 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 > energyDrink0.length - 1 i>energyDrink0.length−1,表示已经选择完数组中的数。

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

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

初始值为 d p 0 j = d p 1 j = 0 dp0j=dp1j=0 dp0j=dp1j=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
相关推荐
LuminousCPP19 分钟前
数据结构-时间与复杂度|时间/空间复杂度 + 两道力扣练习 + 二分查找复盘
c语言·数据结构·经验分享·算法·leetcode
Nil20821 分钟前
leetcode 三数之和
数据结构·算法·leetcode
小小龙学IT41 分钟前
C++ std::vector 底层实现深度解析:内存布局、扩容策略、移动语义与迭代器失效
开发语言·c++·算法
eBest数字化转型方案1 小时前
从工程视角拆解冰柜资产管理:拍照识别 pipeline、纯净度算法与 IoT 选型踩坑
人工智能·物联网·算法
Interview Aid1121 小时前
Roblox OA 面经|小游戏+编程双修,Coding 两题满分通过
算法
闲研随记1 小时前
【文献阅读 ICLR 2026】RL算法:Co-Rewarding
算法
青山木1 小时前
Hot 100 --- 最小栈
java·数据结构·算法·leetcode
疯狂打码的少年1 小时前
【数据结构】栈的应用:表达式求值(后缀表达式)
java·数据结构·笔记·算法
HiDev_2 小时前
【非标自动化】plc执行顺序问题、扫描周期问题
深度学习·算法·机器学习
高洁012 小时前
工信部教考中心证书-人工智能系列本系列
人工智能·python·深度学习·算法