一、摘樱桃 I

一个N x N的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:

  • 0 表示这个格子是空的,所以你可以穿过它。
  • 1 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。
  • -1 表示这个格子里有荆棘,挡着你的路。

你的任务是在遵守下列规则的情况下,尽可能的摘到最多樱桃:

  • 从位置 (0, 0) 出发,最后到达 (N-1, N-1) ,只能向下或向右走,并且只能穿越有效的格子(即只可以穿过值为0或者1的格子);
  • 当到达 (N-1, N-1) 后,你要继续走,直到返回到 (0, 0) ,只能向上或向左走,并且只能穿越有效的格子;
  • 当你经过一个格子且这个格子包含一个樱桃时,你将摘到樱桃并且这个格子会变成空的(值变为0);
  • 如果在 (0, 0) 和 (N-1, N-1) 之间不存在一条可经过的路径,则没有任何一个樱桃能被摘到。
输入: grid =
[[0, 1, -1],[1, 0, -1],[1, 1,  1]]
输出: 5
解释:
玩家从(0,0)点出发,经过了向下走,向下走,向右走,向右走,到达了点(2, 2)。
在这趟单程中,总共摘到了4颗樱桃,矩阵变成了[[0,1,-1],[0,0,-1],[0,0,0]]。
接着,这名玩家向左走,向上走,向上走,向左走,返回了起始点,又摘到了1颗樱桃。
在旅程中,总共摘到了5颗樱桃,这是可以摘到的最大值了

说明:

grid 是一个 N * N 的二维数组,N的取值范围是1 <= N <= 50。
每一个 grid[i][j] 都是集合 {-1, 0, 1}其中的一个数。
可以保证起点 grid[0][0] 和终点 grid[N-1][N-1] 的值都不会是 -1。

方法一:记忆化搜索

思考

一定存在两条路径,但为什么我用了两次 dp 求的结果不对呢?例如这种例子,两次 dp 的结果是 14,但最优的是 15(因为 dp 就是贪心的一种特例,所以第y一次 dp 得到的结果是 13,第二次得到的只能是 1)

[1, 1, 1, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1]
[1, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 1, 1, 1]

参考别人的优秀想法:找两条从 (0, 0) 到 (n-1, n-1) 的路线

思路

如果你不知道两条路线的点一定在同一条对角线上,就不能解出这道题

const int N = 55, INF = INT_MIN/2;
class Solution {public:int n, f[N][N][N][N];int dfs(int x1, int y1, int x2, int y2, vector<vector<int>>& g) {if (x1<0 || x1>=n || x2<0 || x2>=n || y1<0 || y1>=n || y2<0 || y2>=n || g[x1][y1]==-1 || g[x2][y2]==-1) return INF;if (f[x1][y1][x2][y2]!=-1) return f[x1][y1][x2][y2];if (x1==n-1 && x2==n-1 && y1==n-1 && y2==n-1) return f[x1][y1][x2][y2]=g[x1][y1];int mx=INF;mx = max(mx, dfs(x1+1,y1,x2+1,y2,g));mx = max(mx, dfs(x1,y1+1,x2,y2+1,g));mx = max(mx, dfs(x1+1,y1,x2,y2+1,g));mx = max(mx, dfs(x1,y1+1,x2+1,y2,g));if (x1 == x2 && y1 == y2) mx += g[x1][y1];else mx += g[x1][y1] + g[x2][y2];return f[x1][y1][x2][y2] = mx;}int cherryPickup(vector<vector<int>>& grid) {n=grid.size(); memset(f, -1, sizeof f);return max(0, dfs(0,0,0,0,grid));}
};

根据上述对角线定理,可将四维压缩成三维

const int N = 55, INF = INT_MIN/2;
class Solution {public:int n, f[N][N][N][N];int dfs(int x1, int y1, int x2, vector<vector<int>>& g) {int y2=x1+y1-x2;if (x1<0 || x1>=n || x2<0 || x2>=n || y1<0 || y1>=n || y2<0 || y2>=n || g[x1][y1]==-1 || g[x2][y2]==-1)return INF;if (f[x1][y1][x2][y2]!=-1) return f[x1][y1][x2][y2];if (x1==n-1 && x2==n-1 && y1==n-1 && y2==n-1) return f[x1][y1][x2][y2]=g[x1][y1];int mx=INT_MIN/2;mx = max(mx, dfs(x1+1,y1,x2,g)); //1向下,2向右mx = max(mx, dfs(x1,y1+1,x2,g)); //1向右,2向右mx = max(mx, dfs(x1+1,y1,x2+1,g)); //1向下,2向下mx = max(mx, dfs(x1,y1+1,x2+1,g)); //1向右,2向下if (x1 == x2 && y1 == y2) mx += g[x1][y1];else mx += g[x1][y1] + g[x2][y2];return f[x1][y1][x2][y2] = mx;}int cherryPickup(vector<vector<int>>& grid) {n=grid.size(); memset(f, -1, sizeof f);return max(0, dfs(0,0,0,grid));}
};

复杂度分析

  • 时间复杂度: O ( n 3 ) O(n^3) O(n3),
  • 空间复杂度: O ( n 4 ) O(n^4) O(n4),

二、摘樱桃 II

Given a rows x cols matrix grid representing a field of cherries. Each cell in grid represents the number of cherries that you can collect.

You have two robots that can collect cherries for you, Robot #1 is located at the top-left corner (0,0) , and Robot #2 is located at the top-right corner (0, cols-1) of the grid.

Return the maximum number of cherries collection using both robots by following the rules below:

  • From a cell (i,j), robots can move to cell (i+1, j-1) , (i+1, j) or (i+1, j+1).
  • When any robot is passing through a cell, It picks it up all cherries, and the cell becomes an empty cell (0).
  • When both robots stay on the same cell, only one of them takes the cherries.
  • Both robots cannot move outside of the grid at any moment.
  • Both robots should reach the bottom row in the grid.

Input: grid = [[3,1,1],[2,5,1],[1,5,5],[2,1,1]]
Output: 24
Explanation: Path of robot #1 and #2 are described in color green and blue respectively.
Cherries taken by Robot #1, (3 + 2 + 5 + 2) = 12.
Cherries taken by Robot #2, (1 + 5 + 5 + 1) = 12.
Total of cherries: 12 + 12 = 24.
方法一:dfs
  • 机器人可走三个方向:左下、下、右下
  • 而且两个机器人一定在同一层,这一点简化了问题,直接用一个三维数组 int[][][] memo 存储状态,memo[i][j][k] 表示在第 i 层,机器人 1 的横坐标为 j,且机器人 2 的纵坐标为 k 的最大樱桃数。

Q:如果走到同一格怎么处理?
A:我们知道两个机器人走到同一个格 i i i,只有一个能拿到樱桃,这个做法并不如派一个机器人到格子 i i i,然后另一个机器人到另一个格子的做法拿到的樱桃多,所以这也就意味着,想要两个机器人拿到尽量多的樱桃,二者必定不能相碰。

  • 结束条件:当机器人走到了最后一行,返回 0
  • 本层递归的责任:枚举两个机器人的行走可能
  • 返回值:当前层的最大樱桃数
class Solution {int R, C, g[][], f[][][], d[] = {-1, 0, 1};int dfs(int i, int j, int k) {if (i == R-1) return 0;if (f[i][j][k] != 0) return f[i][j][k];int max = 0;for (int p = 0; p < 3; p++)for (int q = 0; q < 3; q++) {int nj = j + d[p], nk = k + d[q];if (nj < 0 || nj >= C || nk < 0 || nk >= C || nj >= nk)continue;max = Math.max(max, dfs(i+1, nj, nk) + g[i+1][nj] + g[i+1][nk]);}return f[i][j][k] = max;}public int cherryPickup(int[][] grid) {g = grid;R = g.length; C = g[0].length;f = new int[R][C][C];return dfs(0, 0, C-1) + g[0][0] + g[0][C-1];}
}

复杂度分析

  • 时间复杂度: O ( . . . ) O(...) O(...),
  • 空间复杂度: O ( . . . ) O(...) O(...),

方法二:dp

记忆化过了,就没想那么多了,状态表示都一模一样。

  • 定义状态

    • f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示在第 i i i 层,机器人 1 的横坐标为 j j j,且机器人 2 的纵坐标为 k k k 的可拿到的最大樱桃数。
  • 思考初始化:
    • f [ 0 ] [ 0 ] [ C − 1 ] = g [ 0 ] [ 0 ] + g [ 0 ] [ C − 1 ] f[0][0][C-1] = g[0][0] + g[0][C-1] f[0][0][C−1]=g[0][0]+g[0][C−1]
  • 思考状态转移方程
    • f [ i ] [ j ] [ k ] = m a x ( m a x , f [ i − 1 ] [ n j ] [ n k ] ) + g [ i ] [ j ] + g [ i ] [ k ] f[i][j][k] = max(max, f[i-1][nj][nk]) + g[i][j] + g[i][k] f[i][j][k]=max(max,f[i−1][nj][nk])+g[i][j]+g[i][k] 表示当前层可摘的最多樱桃数等于上一层的可摘最多樱桃数 + 本层两个机器人位置的樱桃数。
  • 思考输出: f [ R − 1 ] [ 0... C − 1 ] [ 0... C − 1 ] f[R-1][0...C-1][0...C-1] f[R−1][0...C−1][0...C−1]

好吧,打脸,dp 还是很多细节需要处理的,没了下面注释的两行代码,只能过 35/75 测试数据。

class Solution {int R, C, f[][][], d[] = {-1, 0, 1};public int cherryPickup(int[][] g) {R = g.length; C = g[0].length;f = new int[R][C][C];for (int i = 1; i < R; i++)for (int j = 0; j < C; j++)for (int k = 0; k < C; k++) {f[i][j][k] = Integer.MIN_VALUE;}f[0][0][C-1] = g[0][0] + g[0][C-1];for (int i = 1; i < R; i++)for (int j = 0; j < C; j++)for (int k = C-1; k > j; k--) {// if (j >= i + 1 || (C-k-1) >= i + 1)//     continue;int max = 0;for (int p = 0; p < 3; p++)for (int q = 0; q < 3; q++) {int nj = j + d[p], nk = k + d[q];if (nj < 0 || nj >= C || nk < 0 || nk >= C || nj >= nk)continue;max = Math.max(max, f[i-1][nj][nk]);}f[i][j][k] = max + g[i][j] + g[i][k];}int res = 0;for (int j = 0; j < C; j++)for (int k = 0; k < C; k++) {res = Math.max(res, f[R-1][j][k]);}return res;}
}

复杂度分析

  • 时间复杂度: O ( . . . ) O(...) O(...),
  • 空间复杂度: O ( . . . ) O(...) O(...),

【网格 dp】A002_LC_摘樱桃 I~II 传纸条(基于对角线定理(记忆化 / dp))相关推荐

  1. NYOJ 61:传纸条(一)(三维DP)

    传纸条(一) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列 ...

  2. nyoj 61 传纸条(一)双线程DP

    传纸条(一) 时间限制: 2000 ms  |  内存限制: 65535 KB 难度: 5 描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m ...

  3. Codeforces Round #459 (Div. 2) C 思维,贪心 D 记忆化dp

    Codeforces Round #459 (Div. 2) C. The Monster 题意:定义正确的括号串,是能够全部匹配的左右括号串. 给出一个字符串,有 (.). ? 三种字符, ? 可以 ...

  4. I. Space Station(hash记忆化+dp)

    <文章>陆游 文章本天成,妙手偶得之. 粹然无疵瑕,岂复须人为. 君看古彝器,巧拙两无施. 汉最近先秦,固已殊淳漓. 胡部何为者,豪竹杂哀丝. 后夔不复作,千载谁与期? I. Space ...

  5. 【线性 dp】A005_LC_不同的子序列(记忆化 / dp 分类讨论)

    一.Problem 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数. 题目数据保证答案符合 32 位带符号整数范围. 示例 1: 输入:S = "rabbbit ...

  6. 【01 dp】A005_LC_生成数组(暴搜 / 记忆化 / dp)

    一.Problem Given three integers n, m and k. Consider the following algorithm to find the maximum elem ...

  7. [区间记忆化dp入门][Bribe the Prisoners SPOJ - GCJ1C09C][Codeforces Round #505D (rated, Div. 1 + Div. 2, ba]

    Bribe the Prisoners SPOJ - GCJ1C09C 作为这类题代表,f[i][j]代表第i点到第j点单独处理的最值 这题关键:释放某个囚犯后,就把囚犯分成两段,两段互相独立 这类d ...

  8. 【区间dp】uva10003+ uva 1626 括号匹配问题 【有空自己记忆化写一下!!!】

    讲道理,其实我还不是太懂,这个题看到了两种写法 之前大概想的差不多,要这样实现呀: 常规写法,大概n--3 递归写法,稍微好理解一点 好了,接下来自从看了liuchuo的博客我要变身玛丽苏橙色了 题目 ...

  9. 聪聪和可可(记忆化dp+数学期望)

    emmmm 输入格式 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下 ...

最新文章

  1. DBCP使用BasicdataSource连接(两种单例模式-----饿汉和懒汉模式)
  2. 图解Oracle存储过程教程
  3. 用python制作信贷审批监测表
  4. 拦截器如何获取@requestbody_分布式系统中如何优雅地追踪日志(原理篇)
  5. Servlet配置初始化参数/配置参数
  6. python能做什么工作-Python学完工作不好找?看看数据分析可视化都可以做什么
  7. 【python】os 模块使用笔记
  8. 根据crash学习ARM64虚拟地址空间布局
  9. error LNK2005: 已经在 app_launcher.obj 中定义
  10. Java牛客项目课_仿牛客网讨论区_第七章
  11. linux 命令是什么的缩写,Linux一部分命令解释(命令缩写代表什么意思)
  12. catia二次开发招标_CATIA二次开发
  13. android wp主题,WP主题:ebou4
  14. PAT L1-046 整除光棍 光棍当然不是单身汪呢
  15. ifft java_在Matlab中的fft / ifft反卷积
  16. 微信小程序scroll-view实现滚动卡片
  17. 使用jQuery完成下拉菜单
  18. JBoss学习和应用
  19. 基于java实现下载excel
  20. 访问修饰符的真正理解

热门文章

  1. 古筝几种遥指技法训练
  2. 小技巧大用处:微信小程序状态栏设置全攻略
  3. 热水?还是冰水?饮水冷暖须有度
  4. swift5 ios的国际化(本地化,全球化,多语言)(最主要博客)
  5. 传腾讯寻求增持育碧股份,目标是成为最大股东
  6. mysql uuid()冲突_MySQL数据库UUID()函数引起主键冲突问题
  7. Linux系统管理员之网络配置
  8. 【数值分析】学习笔记目录
  9. 啊哈C——学习7.4存储英文人名
  10. 数据结构实验-病人看病模拟程序