Cherry Pickup解题心得
题目复述
输入一个n x n
大小的地图,地图中的每个位置值包含三种值(-1, 0, 1)
,其中:
-1:代表不允许通过
0:代表可以通过
1:代表可以通过,而且路上有个Cherry(樱桃)
例如,输入:
Input: grid =
[[0, 1, -1],
[1, 0, -1],
[1, 1, 1]]
要求从[0, 0]出发到[n-1, n-1],要求只能向右或向下走,一路上收集樱桃,一个单位里的樱桃只能取走一次,如果取走则再次通过则为空;当到达[n-1, n-1]后再向左向上走回[0, 0],一路上同样收集樱桃
输出,求该过程(即从[0, 0]到[n-1, n-1],从[n-1, n-1]到[0, 0])最多可以获得多少樱桃
例如上输出的答案输出:
Output: 5
解题思路
- 最初思考的是实验动态规划计算,从[0, 0]到[n-1, n-1]的最大樱桃值,同时更新地图上经过有樱桃的位置为0,再从[n-1, n-1]到[0, 0]做一次一样的动态规划算法,则两次最优解的和为总体最优
- 结果发现这总单单的将两个最优解和是无法求到最优的解,在leetcode里看他人的解题思路,发现需要将求一条路最大值的动态规划解题思路转变,转变为同时求两条路最大和的动态规划过程:
dpk[x1][x2]:表示路径长度为k的两条路,且两条路同时以[0,0]出发分别以[x1, k−x1]和[x2, k−x2]位置为终点时的樱桃数和最大值则有状态转移方程为:dpk[x1][x2]=max(dpk−1[x1][x2], dpk−1[x1−1][x2], dpk−1[x1][x2−1], dpk−1[x1−1][x2−1]) - 这个过程很抽象,特别是 dpk[x1][x2] 的含义,有点难以理解,可能需要画图进行进一步的理解;总之算法实现时实际使用的是dp[x1][x2],k是多次循环覆盖的过程
- 多看看下面这段三层循环的代码理解,核心表示的就是上面状态转移方程的含义,每个k循环中的dp的含义代表了 dpk ,然后是用当前更新的 dpk 覆盖前一个循环的 dpk−1 的值:
for (int k = 0; k <= maxLen; k++) {
int maxt = min(k, n-1);
for (int x1 = maxt; x1 >= 0; x1--)
for (int x2 = maxt; x2 >= 0; x2--) {
int y1 = k - x1;
int y2 = k - x2;
if (y1 > n || y2 > n) continue;
if (grid[x1][y1] < 0 || grid[x2][y2] < 0) {
dp[x1][x2] = -1;
continue;
}
int cherry = grid[x1][y1] + grid[x2][y2];
if (x1-1 >= 0) dp[x1][x2] = max(dp[x1][x2], dp[x1-1][x2]);
if (x2-1 >= 0) dp[x1][x2] = max(dp[x1][x2], dp[x1][x2-1]);
if (x1-1 >= 0 && x2-1 >= 0) dp[x1][x2] = max(dp[x1][x2], dp[x1-1][x2-1]);
if (dp[x1][x2] == -1) continue;
if (x1 != x2) dp[x1][x2] += cherry;
else dp[x1][x2] += grid[x1][y1];
}
}
- 这里就不贴详细代码了,具体可以看下面我的这个代码连接:
传送门:https://github.com/zhanzongyuan/leetcode/blob/master/741_Cherry%20Pickup.cpp