1301.给你一个正方形字符数组 board ,你从数组最右下方的字符 ‘S’ 出发。
你的目标是到达数组最左上角的字符 ‘E’ ,数组剩余的部分为数字字符 1, 2, …, 9 或者障碍 ‘X’。在每一步移动中,你可以向上、向左或者左上方移动,可以移动的前提是到达的格子没有障碍。
一条路径的 「得分」 定义为:路径上所有数字的和。
请你返回一个列表,包含两个整数:第一个整数是 「得分」 的最大值,第二个整数是得到最大得分的方案数,请把结果对 10^9 + 7 取余。
如果没有任何路径可以到达终点,请返回 [0, 0] 。
示例 1:
输入:board = [“E23”,“2X2”,“12S”]
输出:[7,1]
示例 2:
输入:board = [“E12”,“1X1”,“21S”]
输出:[4,2]
示例 3:
输入:board = [“E11”,“XXX”,“11S”]
输出:[0,0]
- 我的原始人解法:首先如果只求最大值是很简单的,为了方便我看成从左上到右下了,首先定义 f[i][j] 为从位置 (i,j) 到右下 S 的最大值。那么很明显 (i,j) 只可能从左方,左上方以及上方过来。那么状态方程也就是
f[i][j] = f[i-1][j]+f[i][j-1]+f[i-1][j-1]
。但是由于还要求方案数,那就同样用一个二维数组 count[i][j] 来记录到位置 (i,j) 为最大值的方案数。 -
int mod = (int)1e9+7; public int[] pathsWithMaxScore(List<String> board) { int m = board.size(); char[][] b =new char[m][m]; int[][] f = new int[m][m]; int[][] count = new int[m][m]; for(int i=0;i<m;i++){ b[i] = board.get(i).toCharArray(); } // 起点终点赋值为 0;障碍物为负数比如 -1,表示不可到达 for(int i=0;i<m;i++){ for(int j=0;j<m;j++){ if(b[i][j] == 'X'){ f[i][j] = -1; continue; } else if(b[i][j] == 'E'){ f[i][j] = 0; continue; } int top = i>0? f[i-1][j] : -1; int left = j>0? f[i][j-1] : -1; int topLeft = i>0 && j>0? f[i-1][j-1] : -1; int cur = b[i][j] == 'S'?0:b[i][j]-'0'; int max = top >= left?top:left; max = topLeft >= max?topLeft:max; // 看看有几个方案可以取到最大值 if(top==max && max>=0){ count[i][j]+=count[i-1][j]; count[i][j] %= mod; } if(left==max && max>=0){ count[i][j]+=count[i][j-1]; count[i][j] %= mod; } if(topLeft==max && max>=0){ count[i][j]+=count[i-1][j-1]; count[i][j] %= mod; } // 负数的作用就体现在这里,如果为负数说明上,左,左上三个位置要么在墙壁外了 // 要么就是障碍物,那么 (i,j) 肯定也不可到达了,同样赋值 -1; f[i][j] = max < 0?-1:cur + max; f[i][j] %= mod; } } int max = f[m-1][m-1]; // 终点都不可到达那肯定返回 [0,0] 了 if(max < 0){ return new int[]{0,0}; } return new int[]{max,count[m-1][m-1]}; }
- 他人题解:思路大致,不过改用一维数组且代码实现方式稍微不同
-
int INF = Integer.MIN_VALUE; int mod = (int)1e9+7; int n; public int[] pathsWithMaxScore(List<String> board) { n = board.size(); // 将 board 转存成二维数组 char[][] cs = new char[n][n]; for (int i = 0; i < n; i++) { cs[i] = board.get(i).toCharArray(); } // f(i) 代表从右下角到位置 i 的最大得分 int[] f = new int[n * n]; // f(i) 代表从右下角到位置 i 并取到最大得分的方案数量 int[] g = new int[n * n]; for (int i = n - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { int idx = getIdx(i, j); // 一个初始化的状态,如果是在最后一格(起点): // g[idx] = 1 : 代表到达起点的路径只有一条,这样我们就有了一个「有效值」可以滚动下去 // f[idx] = 0 : 代表在起点得分为 0 if (i == n - 1 && j == n - 1) { g[idx] = 1; continue; } // 如果该位置是「障碍点」,那么对应状态为: // g[idx] = 0 : 「障碍点」不可访问,路径为 0 // f[idx] = INF : 「障碍点」不可访问,得分为无效值 if (cs[i][j] == 'X') { f[idx] = INF; continue; } // 如果是第一个格子(终点),这时候位置得分为 0 int val = (i == 0 && j == 0) ? 0 : cs[i][j] - '0'; // u 代表当前位置的「最大得分」;t 代表取得最大得分的「方案数」 int u = INF, t = 0; // 实现不同之处就在于这个 update,先假定一个最大值,方案数初始化为 0, // 然后不断 update,如果最大值改变就更新为那个位置的最大值和方案数,否则方案数 +1 // 如果「下方格子」合法,尝试从「下方格子」进行转移 if (i + 1 < n) { int cur = f[getIdx(i + 1, j)] + val; int cnt = g[getIdx(i + 1, j)]; int[] res = update(cur, cnt, u, t); u = res[0]; t = res[1]; } // 如果「右边格子」合法,尝试从「右边格子」进行转移 if (j + 1 < n) { int cur = f[getIdx(i, j + 1)] + val; int cnt = g[getIdx(i, j + 1)]; int[] res = update(cur, cnt, u, t); u = res[0]; t = res[1]; } // 如果「右下角格子」合法,尝试从「右下角格子」进行转移 if (i + 1 < n && j + 1 < n) { int cur = f[getIdx(i + 1, j + 1)] + val; int cnt = g[getIdx(i + 1, j + 1)]; int[] res = update(cur, cnt, u, t); u = res[0]; t = res[1]; } // 更新 dp 值 f[idx] = u < 0 ? INF : u; g[idx] = t; } } // 构造答案 int[] ans = new int[2]; // 如果终点不可达(动规值为 INF)时,写入 0 ans[0] = f[getIdx(0, 0)] == INF ? 0 : f[getIdx(0, 0)]; // 如果终点不可达(动规值为 INF)时,写入 0 ans[1] = f[getIdx(0, 0)] == INF ? 0 : g[getIdx(0, 0)]; return ans; } // 更新 dp 值 int[] update(int cur, int cnt, int u, int t) { // 起始答案为 [u, t] : u 为「最大得分」,t 为最大得分的「方案数」 int[] ans = new int[]{u, t}; // 如果当前值大于 u,更新「最大得分」和「方案数」 if (cur > u) { ans[0] = cur; ans[1] = cnt; // 如果当前值等于 u,增加「方案数」 } else if (cur == u && cur != INF) { ans[1] += cnt; } ans[1] %= mod; return ans; } // 二维坐标 (x,y) 与 idx 的相互转换 int getIdx(int x, int y) { return x * n + y; } int[] parseIdx(int idx) { return new int[]{idx / n, idx % n}; } 作者:宫水三叶 链接:https://leetcode.cn/leetbook/read/path-problems-in-dynamic-programming/r87czj/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。