问题描述
小U有 n 块地砖,她需要从第一块地砖走到第 n 块地砖。走到第 i 块地砖需要消耗 ai 的体力值。每次,小U可以选择向前走一步或者向前走两步,目的是消耗最少的体力值走到第 n 块地砖。
请你帮忙计算小U走到第 n 块地砖时消耗的最小体力值。
问题分析
小U需要从第 1 块地砖走到第 n 块地砖,每一步都需要消耗一定的体力值 a[i]。每次可以选择走 1 步或 2 步,目标是让体力值的总消耗最小。
问题的本质
小U需要在每一步中选择是走 1 步还是走 2 步,每种选择都伴随着不同的体力值消耗。她的目标是找到一种“全局最优”的走法,即在所有可能的路径中,选出体力值消耗最少的那一种。
问题的特点
- 每块地砖只能走一次(不会回退)。
- 走到第 i 块地砖时,小U的选择只与前一块地砖(或前两块地砖)有关,与更早的状态无关。这种性质使得问题可以用 动态规划 的方式解决。
动态规划分析
-
状态定义
用一个数组 dp[i] 表示小U走到第 i 块地砖时消耗的最小体力值。
-
状态转移方程
- 小U可以从第 i−1 块地砖跳到第 i 块,这需要额外消耗 a[i] 的体力值,此时消耗为 dp[i−1]+a[i]。
- 小U也可以从第 i−2 块地砖跳到第 i 块,这需要额外消耗 a[i] 的体力值,此时消耗为 dp[i−2]+a[i]。
因此,状态转移方程为:
dp[i]=min(dp[i−1]+a[i],dp[i−2]+a[i])
-
初始条件
- 小U站在第 1 块地砖时,不需要消耗体力值,dp[0]=0。
- 小U走到第 2 块地砖的最小消耗就是第 2 块地砖的体力值,dp[1]=a[1]。
-
目标
求 dp[n−1],即走到第 n 块地砖时的最小体力值。
代码实现
def solution(n: int, a: list) -> int:
if n == 1:
return 0
dp = [float('inf')] * n
dp[0] = 0
dp[1] = a[1]
for i in range(2, n):
dp[i] = min(dp[i-1] + a[i], dp[i-2] + a[i])
return dp[n-1]
示例详解
示例 1
输入:
- n=5
- a=[0,3,2,1,0]
我们需要从第 1 块地砖走到第 5 块地砖,按以下步骤求解:
-
初始化:
- dp[0]=0:起点不消耗体力。
- dp[1]=a[1]=3:直接走到第 2 块地砖消耗体力为 3。
-
计算 dp[2] :
- 从 dp[1] 跳到 dp[2]:消耗 dp[1]+a[2]=3+2=5;
- 从 dp[0] 跳到 dp[2]:消耗 dp[0]+a[2]=0+2=2。
因此,dp[2]=min(5,2)=2。
-
计算 dp[3] :
- 从 dp[2] 跳到 dp[3]:消耗 dp[2]+a[3]=2+1=3;
- 从 dp[1] 跳到 dp[3]:消耗 dp[1]+a[3]=3+1=4。
因此,dp[3]=min(3,4)=3。
-
计算 dp[4] :
- 从 dp[3] 跳到 dp[4]:消耗 dp[3]+a[4]=3+0=3;
- 从 dp[2] 跳到 dp[4]:消耗 dp[2]+a[4]=2+0=2。
因此,dp[4]=min(3,2)=2。
最终结果为 dp[4]=2。
示例 2:
-
输入:n=4, a=[0,5,6,0]
-
计算过程:
- dp[1]=5
- dp[2]=min(dp[1]+a[2],dp[0]+a[2])=min(5+6,0+6)=6
- dp[3]=min(dp[2]+a[3],dp[1]+a[3])=min(6+0,5+0)=5
-
输出:5
示例 3:
-
输入:n=6, a=[0,1,2,3,1,0]
-
计算过程:
- dp[1]=1
- dp[2]=min(dp[1]+a[2],dp[0]+a[2])=min(1+2,0+2)=2
- dp[3]=min(dp[2]+a[3],dp[1]+a[3])=min(2+3,1+3)=4
- dp[4]=min(dp[3]+a[4],dp[2]+a[4])=min(4+1,2+1)=3
- dp[5]=min(dp[4]+a[5],dp[3]+a[5])=min(3+0,4+0)=3
-
输出:3
复杂度分析
-
时间复杂度
- 需要对长度为 n 的数组进行一次遍历,时间复杂度为 O(n)。
-
空间复杂度
- 使用了一个长度为 n 的数组 dp,空间复杂度为 O(n)。
- 如果进一步优化,利用滚动数组,只需保存最近两个状态,空间复杂度可以降为 O(1)。
优化版本代码
def solution(n: int, a: list) -> int:
if n == 1:
return 0
prev2 = 0
prev1 = a[1]
for i in range(2, n):
curr = min(prev1 + a[i], prev2 + a[i])
prev2, prev1 = prev1, curr
return prev1
总结
本题使用动态规划求解,通过状态转移方程计算出每一步的最优选择。在实现中,我们还可以通过滚动数组优化空间复杂度,是一道典型的动态规划入门问题。