【LeetCode 2312】卖木头块

题目描述

原题链接: LeetCode.2312 卖木头块

解题思路

  • 每次切割只能完全横切或者竖切,即切成更矮或者更窄的木块;
  • 每次切割后剩余的两部分木块都是更小规模的同类问题,典型的可以用动态规划求解的问题;
  • 具体到单个问题,高x宽y的木块能卖的最多钱数有三种情况:
    • prices数组中正好有对应宽高的价格,不用切割直接卖;
    • 上一刀横切,尝试找到使得[i, y]和[x - i, y]两个矮木块能卖钱数之和最大的i值, 即 $ MAX_{i=1}^{x/2} (dp[i][y] + dp[x - i][y]) $;
    • 上一刀竖切,尝试找到使得[x, i]与[x, y - i]两个窄木块能卖钱数之和最大的i值, 即 $ MAX_{i=1}^{y/2} (dp[x][i] + dp[x][y - i]) $。
  • 根据这三个情况进行记忆化递归处理规模由大到小的问题;或者从小到大按照状态转移方程求得DP数组值。

解题代码

  • 求解规模由大到小, 记忆化递归的解法:

      /**
       * 如果原始prices数组值用Map做哈希表,耗时会在500ms级别
       * 执行用时: 79 ms , 在所有 Java 提交中击败了 41.18% 的用户
       * 内存消耗: 49.92 MB , 在所有 Java 提交中击败了 76.47% 的用户
       */
      public long sellingWood(int m, int n, int[][] prices) {
          long[][] fullPrice = new long[m + 1][n + 1];
          for (int[] p : prices) {
              fullPrice[p[0]][p[1]] = p[2];
          }
          long[][] memo = new long[m + 1][n + 1];
          for (int i = 0; i <= m; i++) {
              Arrays.fill(memo[i], -1);
          }
          return process(m, n, fullPrice, memo);
      }
    
      private long process(int height, int width, long[][] fullPrice, long[][] memo) {
          if (memo[height][width] != -1) {
              return memo[height][width];
          }
          long ans = fullPrice[height][width];
          for (int i = 1; i <= (height >> 1); i++) {
              ans = Math.max(ans, process(i, width, fullPrice, memo) + process(height - i, width, fullPrice, memo));
          }
          for (int i = 1; i <= (width >> 1); i++) {
              ans = Math.max(ans, process(height, i, fullPrice, memo) + process(height, width - i, fullPrice, memo));
          }
          memo[height][width] = ans;
          return ans;
      }
    
  • 求解规模由小到大, 用状态转移方式求DP数组的解法:

      /**
       * 二维动态规划
       * 执行用时: 20 ms , 在所有 Java 提交中击败了 100.00% 的用户
       * 内存消耗: 50.77 MB , 在所有 Java 提交中击败了 23.53% 的用户
       */
      public long sellingWood(int m, int n, int[][] prices) {
          // dp[x][y]表示高为x宽为y的木块能卖的钱数最大值, prices包含的整块卖的金额是初始值
          long[][] dp = new long[m + 1][n + 1];
          for (int[] p : prices) {
              dp[p[0]][p[1]] = p[2];
          }
          for (int i = 1; i <= m; i++) {
              for (int j = 1; j <= n; j++) {
                  long max = dp[i][j];// 用临时变量而不是直接在第三层for循环内部直接更新dp[i][j],性能稍好些(直接更新的耗时是26ms)
                  for (int k = 1; k <= (i >> 1); k++) {
                      max = Math.max(max, dp[k][j] + dp[i - k][j]);
                  }
                  for (int k = 1; k <= (j >> 1); k++) {
                      max = Math.max(max, dp[i][k] + dp[i][j - k]);
                  }
                  dp[i][j] = max;
              }
          }
          return dp[m][n];
      }
    

posted on 2024-03-15 12:55  真不如烂笔头  阅读(42)  评论(0编辑  收藏  举报

导航