【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
相关推荐
東隅已逝,桑榆非晚3 分钟前
C语言预处理详解:从宏到条件编译
c语言·笔记·算法
cpp_25017 分钟前
P10377 [GESP202403 六级] 好斗的牛
数据结构·c++·算法·题解·洛谷·gesp六级
邪修king7 分钟前
C++ 红黑树自平衡核心:旋转变色、规则详解与 STL 选型逻辑
数据结构·c++·b树·算法
随意起个昵称2 小时前
线性dp-计数类题目10(ZBRKA)
算法·动态规划
Navigator_Z8 小时前
LeetCode //C - 1089. Duplicate Zeros
c语言·算法·leetcode
云泽80810 小时前
C++ 可调用对象通关指南:深度解析 Lambda 表达式、function 包装器与 bind 绑定器
开发语言·c++·算法
wlsh1511 小时前
Go 迭代器
算法
语戚11 小时前
力扣 3161. 块放置查询:线段树解法(Java 实现)
java·算法·leetcode·面试·线段树·力扣·
CS创新实验室12 小时前
从顺序表到动态数组:数据结构的永恒基石与现代语言的优雅封装
数据结构·算法
Black蜡笔小新12 小时前
自动化AI算法训练服务器DLTM训推一体化平台助力农业生产管理实现安全智能化
人工智能·算法·自动化