从零学算法1301

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)
      著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值