题目在这->1937. 扣分后的最大得分 - 力扣(LeetCode)
给你一个 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 。
这道题我在刚开始做的时候也没有思路,看了很长时间的题解之后才恍然大悟。这里记录一下我对题解的一些看法和我自己的理解。
记f[i][j]为在第i行选择的第j个分数。再进行状态转移的时候可以枚举第i-1行的某一个格j’。由此就可以得到状态转移方程:
因为第f[i][j]个格子的最大分数要通过遍历第i-1行的每一个格子来确定,因此时间复杂度会很高最终的时间复杂度是O(mn2).所以该状态转移方程并不完善,要进行优化。可以从|j - j‘|来入手。
当 j′≤j 时,∣j−j′∣=j−j′,状态转移方程变为 :
同理,当 j′>j 时,∣j−j′∣=j′−j,状态转移方程变为 :
可以用两个一维数组f[m]和g[m]来进行状态转移。数组f里存放的是当前这一行所有格子上通过扣分操作后可以得到的最大分数。数组g是模拟数组f所表示的那一行的下一行,数组g是依靠数组f进行状态转移的。
在每一行的状态转移时,先正序遍历一次来算出 j′≤j时,每个格子上的最大值。因为正序遍历时j是越来越大的,所以可以求出第j格左边所有格的最大值。用MAX表示,然后在进行状态转移,结果储存到g[j]中。然后在逆序遍历一次来算出j′>j 时,每个格子上的最大值。因为逆序遍历时j是越来越小的,所以可以求出第j格右边所有格的最大值,然后再与在正序遍历中求出的第j格左边所有格的最大值进行比较,进行状态转移,结果存到g[j]中。最后更新数组f。
最后的结果是动态规划完成后数组f中的最大值。
具体代码如下:
class Solution {
public:
long long maxPoints(vector<vector<int>>& points) {
int n = points.size(),m = points[0].size();
vector<long long> f(m,0);
for (int i = 0;i < n;i++)
{
vector<long long> g(m,0);
long long MAX = LLONG_MIN;
//正序遍历
for (int j = 0;j < m;j++)
{
MAX = max(MAX,f[j] + j);
g[j] = max(g[j],MAX + points[i][j] - j);
}
MAX = LLONG_MIN;
//倒序遍历
for (int j = m - 1;j >= 0;j--)
{
MAX = max(MAX,f[j] - j);
g[j] = max(g[j],MAX + points[i][j] + j);
}
f = move(g);
}
return *max_element(f.begin(),f.end());
}
};