最大化数组和的问题
题目描述
题目分析
本题是简单的线性动态规划问题,类似数据分割问题,但本题需要操作所有数组元素。
动态规划状态分析
-
状态定义:
- 用
dp[i]表示考虑到第i个元素后,数组可以达到的最大和。
- 用
-
状态转移方程:
dp[i]=max(dp[i−1]+A[i]×2,dp[i−1]+A[i]÷2)-
这里:
dp[i−1]+A[i]×2:表示选择将A[i]翻倍。dp[i−1]+A[i]÷2:表示选择将A[i]减半。
-
-
边界条件:
-
dp[0]=max(A[0]×2,A[0]÷2)- 第一个元素的操作只有翻倍和减半两种。
-
-
目标:
- 最终的结果是
dp[N−1],即考虑所有元素后的最大和。
- 最终的结果是
Python代码
def solution(N: int, A: list) -> int:
# 初始化 dp 数组
dp = [0] * N
# 初始化第一个元素的选择
dp[0] = max(A[0] * 2, A[0] // 2)
# 逐步计算每个元素的最优选择
for i in range(1, N):
double = A[i] * 2 # 翻倍操作
halve = A[i] // 2 # 减半操作
dp[i] = max(dp[i-1] + double, dp[i-1] + halve)
# 返回最后的最大和
return dp[N-1]
# 测试用例
if __name__ == '__main__':
print(solution(N=3, A=[-4, 2, 4]) == 10) # 示例 1
print(solution(N=2, A=[-8, 4]) == 4) # 示例 2
print(solution(N=4, A=[-6, 6, 2, -2]) == 12) # 示例 3
最优硬币组合问题
题目描述
题目分析
这道题属于完全背包问题的变体,
动态规划状态描述
1. 状态定义
- 定义
dp[i]为「金额为i时,最少的硬币数量」。 - 如果无法组成金额
i,则设定dp[i]=∞。
2. 状态转移方程
-
若选择硬币
array[j]: dp[i]=min(dp[i],dp[i−array[j]]+1)- 意思是:组成金额
i的最少硬币数量,要么不选当前硬币,保留之前的最优解dp[i];要么选择当前硬币array[j],通过减去硬币值递归寻找子问题dp[i−array[j]]。
- 意思是:组成金额
3. 边界条件
dp[0]=0:组成金额为 0 时,所需硬币数为 0。dp[i]=∞(初始化):表示金额i无法被硬币数组组成。
4. 目标
- 通过
dp[total]得到金额total的最少硬币数。 - 若
dp[total]=∞,则表示无法组成目标金额,返回None。
Python代码
def solution(array, total):
array.sort()
dp = [float('inf')] * (total + 1)
dp[0] = 0
# 动态规划来求出最少的元素数
for num in array:
for i in range(num, total + 1):
if dp[i - num] != float('inf'):
dp[i] = min(dp[i], dp[i - num] + 1)
# 如果无法组合成total,返回 None
if dp[total] == float('inf'):
return None
# 逆向查找组成 total 的具体元素
result = []
remaining_total = total
for i in range(len(array) - 1, -1, -1):
while remaining_total >= array[i] and dp[remaining_total] == dp[remaining_total - array[i]] + 1:
result.append(array[i])
remaining_total -= array[i]
return result
if __name__ == "__main__":
# 测试用例
print(solution([1, 2, 3, 10], 21) == [10, 10, 1]) # 输出: True
print(solution([1, 2, 5], 18) == [5, 5, 5, 2, 1]) # 输出: True