华为OD机试:双路径动态规划获取最多糖果
·
1. 题目背景与核心需求解析
这道来自华为OD机试的编程题目,本质上是一个典型的图论与动态规划结合的应用场景。题目描述了一位家长带着孩子在矩阵地图上移动,目标是找到一条从起点到终点的路径,使得在限定步数内能够获取最多的糖果。这类问题在实际开发中非常常见,比如游戏中的AI寻路算法、物流配送的最优路径规划等。
1.1 题目要素拆解
题目包含几个关键约束条件:
- 二维矩阵地图(M行N列),每个格子包含糖果数量
- 家长和小孩各自有独立的移动路径
- 总移动步数限制为K
- 移动规则:每次只能向右或向下移动
- 计分规则:若两人同格子,只计一次糖果;不同格子则累加
1.2 问题转化思路
这实际上是一个双线程动态规划问题。我们需要同时追踪两个移动主体的位置状态,并考虑以下特殊情况:
- 路径交叉时的糖果去重
- 步数限制下的最优解搜索
- 移动方向约束带来的状态转移限制
2. 算法设计与复杂度分析
2.1 动态规划状态定义
采用四维DP数组来记录状态:
dp[k][x1][y1][x2][y2]
# 表示走了k步后,家长在(x1,y1),孩子在(x2,y2)时的最大糖果数
实际实现时可以通过步数k的递推关系降维,优化空间复杂度:
dp[x1][y1][x2][y2]
# 使用两个二维数组交替更新
2.2 状态转移方程
对于每个可能的状态转移,需要考虑四种移动组合(家长右/下 × 孩子右/下):
# 家长向右,孩子向下
dp[x1][y1][x2][y2] = max(
dp[x1][y1][x2][y2],
dp[x1-1][y1][x2][y2-1] + current_candy
)
2.3 复杂度优化技巧
- 对称性剪枝 :当家长和孩子位置互换时结果相同,可减少一半计算量
- 边界处理 :预先处理矩阵边缘的移动限制
- 糖果去重判断 :通过坐标比较决定是否重复计算
const samePos = (x1 === x2 && y1 === y2); const total = samePos ? candy[x1][y1] : candy[x1][y1] + candy[x2][y2];
3. Python实现详解
3.1 核心数据结构
def max_candy(matrix, K):
m, n = len(matrix), len(matrix[0])
# 使用字典保存状态更节省空间
dp = {}
dp[(0,0,0,0)] = matrix[0][0] if m > 0 and n > 0 else 0
3.2 递推过程实现
for step in range(1, K+1):
new_dp = {}
for (x1,y1,x2,y2), candy in dp.items():
# 生成所有可能的移动组合
moves = [(0,1),(1,0)]
for dx1, dy1 in moves:
for dx2, dy2 in moves:
nx1, ny1 = x1+dx1, y1+dy1
nx2, ny2 = x2+dx2, y2+dy2
# 边界检查
if 0<=nx1<m and 0<=ny1<n and 0<=nx2<m and 0<=ny2<n:
# 糖果计算
if (nx1,ny1) == (nx2,ny2):
new_candy = candy + matrix[nx1][ny1]
else:
new_candy = candy + matrix[nx1][ny1] + matrix[nx2][ny2]
# 更新状态
key = (nx1,ny1,nx2,ny2)
if key not in new_dp or new_candy > new_dp[key]:
new_dp[key] = new_candy
dp = new_dp
3.3 结果提取与优化
max_candies = 0
for (x1,y1,x2,y2), candy in dp.items():
# 检查是否到达终点
if (x1 == m-1 and y1 == n-1 and x2 == m-1 and y2 == n-1):
max_candies = max(max_candies, candy)
return max_candies
4. JavaScript实现要点
4.1 性能优化策略
由于JS的对象处理性能问题,建议:
- 使用坐标压缩技巧:将(x1,y1,x2,y2)转为字符串作为key
const getKey = (x1,y1,x2,y2) => `${x1},${y1},${x2},${y2}`; - 优先使用Map而不是普通对象
let dp = new Map(); dp.set(getKey(0,0,0,0), matrix[0][0]);
4.2 完整实现示例
function maxCandy(matrix, K) {
const m = matrix.length, n = matrix[0].length;
let dp = new Map();
dp.set(getKey(0,0,0,0), matrix[0][0]);
for(let step=1; step<=K; step++) {
let newDp = new Map();
for(let [key, val] of dp) {
let [x1,y1,x2,y2] = key.split(',').map(Number);
// 生成移动方向
const directions = [[0,1],[1,0]];
directions.forEach(([dx1, dy1]) => {
directions.forEach(([dx2, dy2]) => {
const nx1 = x1+dx1, ny1 = y1+dy1;
const nx2 = x2+dx2, ny2 = y2+dy2;
if(nx1>=0 && nx1<m && ny1>=0 && ny1<n &&
nx2>=0 && nx2<m && ny2>=0 && ny2<n) {
const newKey = getKey(nx1,ny1,nx2,ny2);
const samePos = (nx1===nx2 && ny1===ny2);
const newVal = val + (samePos ?
matrix[nx1][ny1] :
matrix[nx1][ny1] + matrix[nx2][ny2]);
if(!newDp.has(newKey) || newVal > newDp.get(newKey)) {
newDp.set(newKey, newVal);
}
}
});
});
}
dp = newDp;
}
let max = 0;
for(let [key, val] of dp) {
const [x1,y1,x2,y2] = key.split(',').map(Number);
if(x1===m-1 && y1===n-1 && x2===m-1 && y2===n-1) {
max = Math.max(max, val);
}
}
return max;
}
5. 常见问题与调试技巧
5.1 内存溢出处理
当矩阵较大(如50×50)且K值较大时,可能出现内存问题。解决方法:
- 使用滚动数组技术,只保留上一步的状态
- 剪枝策略:丢弃明显不会成为最优解的状态
if new_candy < current_max - threshold: continue
5.2 边界条件测试用例
必须测试的特殊情况:
- 1×1矩阵:直接返回唯一格子的值
- 步数K不足以到达终点的情况
- 矩阵中有负数的糖果值(题目通常保证非负)
- 家长和孩子初始位置相同的情况
5.3 调试日志建议
在关键位置添加状态打印:
console.log(`Step ${step}:`, Array.from(dp.entries()));
6. 算法优化进阶思路
6.1 A*启发式搜索
对于大规模矩阵,可以考虑:
- 设计启发式函数估计剩余路径的最大可能糖果
- 优先扩展最有希望的路径
# 估计函数示例 def heuristic(x, y): return suffix_max[x][y] # 预计算每个位置到终点的最大糖果
6.2 并行计算优化
利用多线程特性:
- 将状态空间划分为多个区域并行处理
- 注意线程间共享数据的同步问题
6.3 机器学习预测
对于超大规模问题:
- 训练神经网络预测最优路径模式
- 使用预测结果指导搜索方向
7. 实际应用场景扩展
这类算法不仅用于机试题目,还可应用于:
- 游戏开发中的双角色协作AI
- 物流配送中的多车路径优化
- 自动化仓储系统中的多机器人调度
- 交通管制中的多车辆路线规划
在华为的实际业务中,类似算法可能用于:
- 网络设备的多路径数据传输优化
- 分布式计算任务调度
- 5G网络资源分配策略
更多推荐

所有评论(0)