LeetCode——1937. 扣分后的最大得分(Maximum Number of Points with Cost)[中等]——分析及代码(Java)

本文介绍了如何使用动态规划解决LeetCode中的1937题,涉及矩阵操作与相邻行得分减损规则。通过计算每行格子选择对总分的影响,逐步优化策略,找到在考虑成本后的最大得分。代码实现展示了如何预处理并求解最优路径。
摘要由CSDN通过智能技术生成

LeetCode——1937. 扣分后的最大得分[Maximum Number of Points with Cost][中等]——分析及代码[Java]

一、题目

给你一个 m x n 的整数矩阵 points (下标从 0 开始)。一开始你的得分为 0 ,你想最大化从矩阵中得到的分数。

你的得分方式为:每一行 中选取一个格子,选中坐标为 (r, c) 的格子会给你的总得分 增加 points[r][c] 。

然而,相邻行之间被选中的格子如果隔得太远,你会失去一些得分。对于相邻行 r 和 r + 1 (其中 0 <= r < m - 1),选中坐标为 (r, c1) 和 (r + 1, c2) 的格子,你的总得分 减少 abs(c1 - c2) 。

请你返回你能得到的 最大 得分。

abs(x) 定义为:

  • 如果 x >= 0 ,那么值为 x 。
  • 如果 x < 0 ,那么值为 -x 。

示例 1:

输入:points = [[1,2,3],[1,5,1],[3,1,1]]
输出:9
解释:
最优方案选中的格子,坐标分别为 (0, 2),(1, 1) 和 (2, 0) 。
你的总得分增加 3 + 5 + 3 = 11 。
但是你的总得分需要扣除 abs(2 - 1) + abs(1 - 0) = 2 。
你的最终得分为 11 - 2 = 9 。

示例 2:

输入:points = [[1,5],[2,3],[4,2]]
输出:11
解释:
最优方案选中的格子,坐标分别为 (0, 1),(1, 1) 和 (2, 0) 。
你的总得分增加 5 + 3 + 4 = 12 。
但是你的总得分需要扣除 abs(1 - 1) + abs(1 - 0) = 1 。
你的最终得分为 12 - 1 = 11 。

提示:

  • m == points.length
  • n == points[r].length
  • 1 <= m, n <= 10^5
  • 1 <= m * n <= 10^5
  • 0 <= points[r][c] <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-number-of-points-with-cost
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、分析及代码

1. 动态规划

(1)思路

根据题意,依次在每一行中选取格子后,所得到的分数实际上只和上一行选取格子后得到的总分有关,因此可结合动态规划方法求解。

设计一个数组 dp[i],表示当前行第 i 个格子被选中时的最大得分。若上一行对应位置的总分为 lastLine[i],则 dp[i] = Math.max(lastLine[j] - |i - j|) + points[line][i], 0 <= j < n。

为简化每个格子最高分数的求解,可进一步结合动态规划方法,预处理计算各个位置选取上一行的左、右侧格子所能得到的最高分。

完成全部遍历后,dp[i] 即为最后一行各个格子被选取时得到的最高分,最大值就是所求解。需要注意的是,本题的数据范围可能超出 int,因此全程需采用 long 结构编写代码。

(2)代码

class Solution {
    public long maxPoints(int[][] points) {
        //初始化
        int m = points.length, n = points[0].length;
        long[] dp = new long[n];//dp[i]表示当前行第i个格子被选中时的最大得分
        for (int i = 0; i < n; i++)
            dp[i] = points[0][i];//第0行格子的最大得分就是对应格子的分数
        
        //动态规划
        for (int i = 1; i < m; i++) {
            long[] lastLine = dp.clone();//复制得到上一行各个格子的最大得分

            //预处理计算各个位置选取上一行的左、右侧格子所能得到的最高分,为简化代码,当前位置正上方的格子统计在左侧中
            long[] left = new long[n], right = new long[n];
            left[0] = lastLine[0];
            for (int j = 1; j < n; j++)
                left[j] = Math.max(left[j - 1] - 1, lastLine[j]);
            right[n - 1] = 0;
            for (int j = n - 2; j >= 0; j--)
                right[j] = Math.max(right[j + 1] - 1, lastLine[j + 1] - 1);
            
            //计算各个格子的最大得分
            for (int j = 0; j < n; j++)
                dp[j] = Math.max(left[j], right[j]) + points[i][j];
        }

        //统计答案,即求最后一行中各个格子的最大得分
        long ans = 0L;
        for (int i = 0; i < n; i++)
            ans = Math.max(ans, dp[i]);
        return ans;
    }
}

(3)结果

执行用时 :20 ms,在所有 Java 提交中击败了 100.00% 的用户;
内存消耗 :68.6 MB,在所有 Java 提交中击败了 100.00% 的用户。
(目前提交用户量不足,暂无排名)

三、其他

暂无。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值