动态规划
小U的最大连续移动次数问题
问题描述
小U决定在一个 m×nm×n 的地图上行走。地图中的每个位置都有一个高度,表示地形的高低。小U只能在满足以下条件的情况下移动:
- 只能上坡或者下坡,不能走到高度相同的点。
- 移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡。
- 每个位置只能经过一次,不能重复行走。
任务是帮助小U找到他在地图上可以移动的最大次数,即在符合所有条件的前提下,小U能走过的最大连续位置数量。
例如在以下2x2的地图中:
1 2
4 3
中庸行者可以选择移动顺序 3 -> 4 -> 1 -> 2,最大移动次数为3。
测试样例
样例1:
输入:
m = 2, n = 2, a = [[1, 2], [4, 3]]
输出:3
样例2:
输入:
m = 3, n = 3, a = [[10, 1, 6], [5, 9, 3], [7, 2, 4]]
输出:8
样例3:
输入:
m = 4, n = 4, a = [[8, 3, 2, 1], [4, 7, 6, 5], [12, 11, 10, 9], [16, 15, 14, 13]]
输出:11
解题思路
这是一个图的遍历问题,可以通过深度优先搜索(DFS)来求解,同时结合回溯以保证每个位置只能被访问一次。
为了优化效率,可以引入记忆化搜索(Memoization)记录从某点开始,分别以“上坡”或“下坡”为起点的最长路径长度,避免重复计算。
问题核心:
- 路径状态的维护:路径的交替性要求我们在每次移动中记录状态(当前是上坡还是下坡)。
- 避免重复计算:在尝试所有起点时,路径会出现大量重复计算,需要通过记忆化搜索优化。
- 方向遍历:每个点最多向上下左右四个方向移动。
算法步骤
- 数据结构设计
- 使用二维数组
dp[x][y][state]:dp[x][y][1]表示从位置(x, y)出发,当前状态为“上坡”时的最长路径长度。dp[x][y][0]表示从位置(x, y)出发,当前状态为“下坡”时的最长路径长度。
- 使用方向数组
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]表示上下左右四个方向。
- DFS 定义与递归逻辑
- 递归结束条件:如果当前点
(x, y)的某个状态已经计算过(在dp中有值),直接返回。 - 遍历相邻点:
- 对每个相邻点
(nx, ny):- 检查边界条件(是否在地图范围内);
- 检查高度条件:
- 如果当前是“上坡”,则下一点高度必须更高;
- 如果当前是“下坡”,则下一点高度必须更低。
- 如果满足条件,则递归计算以
(nx, ny)为起点的最长路径长度。
- 回溯与结果更新:
- 每次递归返回后更新当前点的最大路径长度。
- 将结果存储到
dp[x][y][state]中,避免重复计算。
- 遍历地图所有点
- 从地图的每个点
(i, j)开始,分别尝试以“上坡”和“下坡”为起点,计算最长路径长度。 - 记录所有起点的最大值作为最终结果。
代码
def solution(m: int, n: int, a: list) -> int:
# 初始化访问标记数组
visited = [[False] * n for _ in range(m)]
# 定义DFS函数
def dfs(x, y, is_uphill):
# 标记当前位置为已访问
visited[x][y] = True
# 初始化当前路径的最大移动次数
max_path_moves = 0
# 遍历四个方向
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = x + dx, y + dy
if 0 <= nx < m and 0 <= ny < n and not visited[nx][ny]:
# 判断是否满足交替上坡/下坡的条件
if is_uphill and a[nx][ny] > a[x][y]:
max_path_moves = max(max_path_moves, 1 + dfs(nx, ny, False))
elif not is_uphill and a[nx][ny] < a[x][y]:
max_path_moves = max(max_path_moves, 1 + dfs(nx, ny, True))
# 回溯:标记当前位置为未访问
visited[x][y] = False
return max_path_moves
# 初始化最大移动次数
max_moves = 0
# 从每个位置开始进行DFS
for i in range(m):
for j in range(n):
max_moves = max(max_moves, dfs(i, j, True))
max_moves = max(max_moves, dfs(i, j, False))
return max_moves
if __name__ == '__main__':
print(solution(2, 2, [[1, 2], [4, 3]]) == 3)
print(solution(3, 3, [[10, 1, 6], [5, 9, 3], [7, 2, 4]]) == 8)
print(solution(4, 4, [[8, 3, 2, 1], [4, 7, 6, 5], [12, 11, 10, 9], [16, 15, 14, 13]]) == 11)
01背包最大价值问题
问题描述
一个旅行者外出旅行时需要将 n 件物品装入背包,背包的总容量为 m。每个物品都有一个重量和一个价值。你需要根据这些物品的重量和价值,决定如何选择物品放入背包,使得在不超过总容量的情况下,背包中物品的总价值最大。
给定两个整数数组 weights 和 values,其中 weights[i] 表示第 i 个物品的重量,values[i] 表示第 i 个物品的价值。你需要输出在满足背包总容量为 m 的情况下,背包中物品的最大总价值。
测试样例
样例1:
输入:
n = 3 ,weights = [2, 1, 3] ,values = [4, 2, 3] ,m = 3
输出:6
样例2:
输入:
n = 4 ,weights = [1, 2, 3, 2] ,values = [10, 20, 30, 40] ,m = 5
输出:70
样例3:
输入:
n = 2 ,weights = [1, 4] ,values = [5, 10] ,m = 4
输出:10
解题思路
这是一个经典的01背包问题,用动态规划解决即可
对于每件物品,有两种选择:
- 不放入背包。
- 放入背包
目标是找到一种选择,使得在总重量不超过 mmm 的情况下,背包中的物品总价值最大
算法步骤
- 定义状态:
- 用
dp[j]表示背包容量为 jjj 时,背包可以达到的最大总价值。 - 初始化
dp[0] = 0,即背包容量为 0 时,总价值为 0。
- 用
- 状态转移方程:
- 对于第 i 件物品,其重量为
weights[i],价值为values[i]:- 如果不选这件物品:
dp[j] = dp[j](状态保持不变)。 - 如果选这件物品:
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]),即比较选与不选的总价值。
- 如果不选这件物品:
- 从容量 m 倒序更新到
weights[i],避免重复使用同一物品。
- 对于第 i 件物品,其重量为
代码
``python def solution(n: int, weights: list, values: list, m: int) -> int: dp = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(m + 1):
if weights[i - 1] > j:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
return dp[n][m]
if name == 'main': print(solution(n = 3, weights = [2, 1, 3], values = [4, 2, 3], m = 3) == 6) print(solution(n = 4, weights = [1, 2, 3, 2], values = [10, 20, 30, 40], m = 5) == 70) print(solution(n = 2, weights = [1, 4], values = [5, 10], m = 4) == 10)