最大化从给定矩阵的左上角和右上角到达最底行的成本
给定一个大小为M * N的矩阵grid[][] ,其中矩阵的每个单元格表示该单元格上存在的成本。任务是最大化从矩阵的左上角和右上角移动到最底行的成本,其中每一步:
- 从单元格(i, j)可以移动到(i+1, j-1) 、 (i+1, j)或(i+1, j+1) 。
- 如果两个点同时在同一个单元格中,则单元格的成本将仅增加一个。
- 在任何情况下,这些点都可以移出网格。
例子:
Input:
grid = {{3, 1, 1},
{2, 5, 1},
{1, 5, 5},
{2, 1, 1}}
Output: 24
Explanation: Path from top-left and top-right are described in color orange and blue respectively.
Cost for first path is (3 + 2 + 5 + 2) = 12.
Cost for second path is (1 + 5 + 5 + 1) = 12.
Total cost is 12 + 12 = 24.
Input:
grid = {{1, 0, 0, 0, 0, 0, 1},
{2, 0, 0, 0, 0, 3, 0},
{2, 0, 9, 0, 0, 0, 0},
{0, 3, 0, 5, 4, 0, 0},
{1, 0, 2, 3, 0, 0, 6}}
Output: 28
Explanation: Path from top-left and top-right are described in color orange and blue respectively.
Cost of the first path is (1 + 9 + 5 + 2) = 17.
Cost of the second path is (1 + 3 + 4 + 3) = 11.
Total cost of the paths is 17 + 11 = 28.
This is the maximum cost possible.
直觉:将左上角的点表示为point1 ,将右上角的点表示为point2 。以下是解决问题背后的直觉。
- 请注意,它们的移动顺序无关紧要,因为它不会影响最终结果。最终成本取决于点的轨迹。因此,运动可以按任何顺序进行。有申请DP的需求,所以找一个适合DP的订单。在这里尝试一些可能的移动订单。
Can point1 be moved firstly to the bottom row, and then point2?
Maybe not. In this case, the movement of point1 will impact the movement of point2. In other words, the optimal track of point2 depends on the track of point1. In this case there will be requirement to record the whole track of point1 as the state for point2 in DP. The number of sub-problems is too much.
In fact, in any case, when anyone point is moved several steps earlier than the other, the movement of the first point will impact the movement of the other point. So both the points should be moved synchronously.
- 将 DP 状态定义为(row1, col1, row2, col2) ,其中(row1, col1)表示point1的位置, (row2, col2)表示point2的位置。如果它们同步移动,则两个点将始终位于同一行。因此, row1 = row2 。
让row = row1 = row2 。 DP 状态被简化为(row, col1, col2) ,其中(row, col1)表示point1的位置, (row, col2)表示point2的位置。 - 所以对于 DP函数:让dp(row, col1, col2)返回最大成本,如果point1从(row, col1)开始并且point2从(row, col2)开始。
- 基本情况是两个点都从底线开始。在这种情况下,不需要移动,只考虑当前单元格的成本。如果点在完全相同的单元格处,请记住不要重复计算。
- 在其他情况下,添加未来路径的最大成本。过渡函数来了。由于点可以同步移动,并且每个点在一个步骤中具有三种不同的运动,因此两个机器人总共有 3*3 = 9 种可能的运动:
Sl. No. | Point1 movement | Point2 movement |
---|---|---|
1 | Left Down | Left Down |
2 | Left Down | Down |
3 | Left Down | Right Down |
4 | Down | Left Down |
5 | Down | Down |
6 | Down | Right Down |
7 | Right Down | Left Down |
8 | Right Down | Down |
9 | Right Down | Right Down |
- 未来路径的最大成本将是这 9 个动作中的最大值,即
dp(row+1, new_col1, new_col2) ,其中new_col1可以是col1 、 col1+1或col1-1 ,而new_col2可以是col2 、 col2+1或col2-1 。
方法 1 – 动态规划(自下而上):问题解决方案基于动态规划概念并使用上述直觉。
- 定义一个dp函数,它将三个整数row、col1 和 col2作为输入。
- (row, col1)表示point1的位置, (row, col2)表示point2的位置。
- 如果point1从(row, col1)开始并且point2从(row, col2)开始,则 dp函数返回最大成本。
- 在dp函数中:
- 在(row, col1)和(row, col2)处添加成本。如果col1 = col2则不要重复计算。
- 如果没有达到最后一行,则添加未来路径中可以获得的最大成本。
- 未来能达到的最大代价是dp(row+1, new_col1, new_col2)的最大值,其中new_col1可以是col1, col1+1, or col1-1 , new_col2可以是col2, col2+1,或 col2-1 。
- 返回总成本。
- 最后,在 main函数中返回dp(row=0, col1=0, col2=last_column) 。
下面是上述方法的实现。
C++
// C++ code to implement the approach
#include
using namespace std;
// Dp function
int dp(int row, int col1, int col2,
vector >& grid,
vector > >& dpCache)
{
if (col1 < 0 || col1 >= grid[0].size()
|| col2 < 0 || col2 >= grid[0].size())
return 0;
// Check cache
if (dpCache[row][col1][col2] != -1)
return dpCache[row][col1][col2];
// Add cost of the current cell
int result = grid[row][col1];
if (col1 != col2)
result
+= grid[row][col2];
// DP transition
if (row != grid.size() - 1) {
int maximum = 0;
for (int newCol1 = col1 - 1;
newCol1 <= col1 + 1; newCol1++)
for (int newCol2 = col2 - 1;
newCol2 <= col2 + 1;
newCol2++)
maximum
= max(maximum,
dp(row + 1, newCol1,
newCol2, grid,
dpCache));
result += maximum;
}
dpCache[row][col1][col2] = result;
return result;
}
// Function to maximize the cost
int pickup(vector >& grid)
{
int M = grid.size();
if (M == 0)
return 0;
int N = grid[0].size();
if (N == 0)
return 0;
vector > >
dpCache(M, vector >(
N, vector(N, -1)));
return dp(0, 0, N - 1, grid, dpCache);
}
// Driver code
int main()
{
vector > grid{
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
cout << pickup(grid);
return 0;
}
Java
// Java code to implement the approach
import java.util.*;
class GFG{
static int[][] grid = {
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
static int[][][] dpCache;
// Dp function
static int dp(int row, int col1, int col2)
{
if (col1 < 0 || col1 >= grid[0].length
|| col2 < 0 || col2 >= grid[0].length)
return 0;
// Check cache
if (dpCache[row][col1][col2] != -1)
return dpCache[row][col1][col2];
// Add cost of the current cell
int result = grid[row][col1];
if (col1 != col2)
result
+= grid[row][col2];
// DP transition
if (row != grid.length - 1) {
int maximum = 0;
for (int newCol1 = col1 - 1;
newCol1 <= col1 + 1; newCol1++)
for (int newCol2 = col2 - 1;
newCol2 <= col2 + 1;
newCol2++)
maximum
= Math.max(maximum,
dp(row + 1, newCol1,
newCol2));
result += maximum;
}
dpCache[row][col1][col2] = result;
return result;
}
// Function to maximize the cost
static int pickup()
{
int M = grid.length;
if (M == 0)
return 0;
int N = grid[0].length;
if (N == 0)
return 0;
dpCache = new int[M][N][N];
for(int i = 0; i < M; i++)
{
for(int j = 0; j < N; j++)
{
for(int l = 0; l < N; l++)
dpCache[i][j][l] = -1;
}
}
return dp(0, 0, N - 1);
}
// Driver code
public static void main(String[] args)
{
System.out.print(pickup());
}
}
// This code is contributed by Rajput-Ji
Python3
# python3 code to implement the approach
# Dp function
def dp(row, col1, col2, grid, dpCache):
if (col1 < 0 or col1 >= len(grid[0])
or col2 < 0 or col2 >= len(grid[0])):
return 0
# Check cache
if (dpCache[row][col1][col2] != -1):
return dpCache[row][col1][col2]
# Add cost of the current cell
result = grid[row][col1]
if (col1 != col2):
result += grid[row][col2]
# DP transition
if (row != len(grid) - 1):
maximum = 0
for newCol1 in range(col1-1, col1 + 2):
for newCol2 in range(col2-1, col2+2):
maximum = max(maximum,
dp(row + 1, newCol1,
newCol2, grid,
dpCache))
result += maximum
dpCache[row][col1][col2] = result
return result
# Function to maximize the cost
def pickup(grid):
M = len(grid)
if (M == 0):
return 0
N = len(grid[0])
if (N == 0):
return 0
dpCache = [[[-1 for _ in range(N)] for _ in range(N)] for _ in range(M)]
return dp(0, 0, N - 1, grid, dpCache)
# Driver code
if __name__ == "__main__":
grid = [
[3, 1, 1], [2, 5, 1],
[1, 5, 5], [2, 1, 1]
]
print(pickup(grid))
# This code is contributed by rakeshsahni
Javascript
C++
// C++ code to implement the approach
#include
using namespace std;
// Function to maximize the cost
int pickup(vector >& grid)
{
int M = grid.size();
if (M == 0)
return 0;
int N = grid[0].size();
if (N == 0)
return 0;
vector > > dp(
M, vector >(
N, vector(N, INT_MIN)));
dp[0][0][N - 1] = grid[0][0]
+ grid[0][N - 1];
for (int i = 1; i < M; i++)
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
for (int l = a - 1; l
<= a + 1;
l++)
for (int r = b - 1;
r <= b + 1; r++) {
if (l < 0 || l >= N
|| r < 0
|| r >= N)
continue;
dp[i][a][b] = max(
dp[i][a][b],
((a != b)
? grid[i][a]
+ grid[i][b]
: grid[i][a])
+ dp[i - 1][l][r]);
}
for (int i =0 ;i > grid{
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
cout << pickup(grid);
return 0;
}
Python3
# Python code to implement the approach
# Function to maximize the cost
import sys
def pickup(grid):
M = len(grid)
if (M == 0):
return 0
N = len(grid[0])
if (N == 0):
return 0
dp = [[[(-sys.maxsize-1) for i in range(N)] for j in range(N)] for k in range(M)]
dp[0][0][N - 1] = grid[0][0] + grid[0][N - 1]
for i in range(1,M):
for a in range(N):
for b in range(N):
for l in range(a - 1,a + 2):
for r in range(b - 1,b + 2):
if (l < 0 or l >= N or r < 0 or r >= N):
continue
dp[i][a][b] = max(dp[i][a][b],(grid[i][a]+ grid[i][b] if (a != b) else grid[i][a]) + dp[i - 1][l][r])
ans = 0
for a in range(N):
for b in range(N):
ans = max(ans, dp[M - 1][a][b])
return ans
# Driver code
grid = [[ 3, 1, 1 ], [ 2, 5, 1 ],
[ 1, 5, 5 ], [ 2, 1, 1 ]]
print(pickup(grid))
# This code is contributed by shinjanpatra
C#
// C# code to implement the approach
using System;
class GFG
{
// Function to maximize the cost
static int pickup(int[,] grid)
{
int M = grid.GetLength(0);
if (M == 0)
return 0;
int N = grid.GetLength(1);
if (N == 0)
return 0;
int [,,]dp = new int[M,N,N];
dp[0,0,N - 1] = grid[0,0]
+ grid[0,N - 1];
for (int i = 1; i < M; i++)
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
for (int l = a - 1; l
<= a + 1;
l++)
for (int r = b - 1;
r <= b + 1; r++) {
if (l < 0 || l >= N
|| r < 0
|| r >= N)
continue;
dp[i,a,b] = Math.Max(
dp[i,a,b],
((a != b)
? grid[i,a]
+ grid[i,b]
: grid[i,a])
+ dp[i - 1,l,r]);
}
int ans = 0;
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
ans = Math.Max(ans, dp[M - 1,a,b]);
return ans;
}
// Driver code
public static void Main()
{
int[,] grid = {
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
Console.WriteLine( pickup(grid));
}}
// This code is contributed by ukasp.
Javascript
C++
// C++ code to implement the approach
#include
using namespace std;
// Function to maximize the cost
int pickup(vector >& grid)
{
int M = grid.size();
if (M == 0)
return 0;
int N = grid[0].size();
if (N == 0)
return 0;
vector > > dp(
2, vector >(
N, vector(N, INT_MIN)));
dp[0][0][N - 1] = grid[0][0]
+ grid[0][N - 1];
// Looping over all rows
for (int i = 1; i < M; i++)
// looping over every cell
// in the row for point1
for (int a = 0; a < N; a++)
// looping over every cell
// in the row for point2
for (int b = 0; b < N; b++)
// Capturing possible
// movements of point 1
for (int l = a - 1;
l <= a + 1; l++)
// Capturing possible
// movements of point2
for (int r = b - 1;
r <= b + 1; r++) {
if (l < 0 || l >= N
|| r < 0 || r >= N)
continue;
// Apply DP transition
dp[i % 2][a][b] = max(
dp[i % 2][a][b],
((a != b)
? grid[i][a]
+ grid[i][b]
: grid[i][a])
+ dp[(i - 1) % 2][l][r]);
}
// Loop over dp to get the final answer
int ans = 0;
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
ans = max(ans,
dp[(M - 1) % 2][a][b]);
return ans;
}
// Driver code
int main()
{
vector > grid{
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
cout << pickup(grid);
return 0;
}
24
时间复杂度: O(M * N 2 )
辅助空间: O(M * N 2 )
方法 2 – 动态规划(自上而下):该解决方案也基于使用上述直觉的动态规划方法。唯一的区别是这里它使用了自顶向下的方法。
- 定义一个三维dp数组,其中每个维度的大小分别为 M、N 和 N,类似于方法 1。
- 这里dp[row][col1][col2]表示在(row, col1)到达point1和在(row, col2)位置到达point2时的最大成本。
- 计算 dp[row][col1][col2](转换方程):
- 在 (row, col1) 和 (row, col2) 处添加成本。如果 col1 = col2,则不要重复计算。
- 如果不在第一行,则添加已访问路径的最大成本。
- 最后,返回最后一行的最大值。
注意:状态压缩可用于保存第一个维度: dp[col1][col2] 。只需在迭代一行后重用dp数组。
实现 1:无状态压缩
C++
// C++ code to implement the approach
#include
using namespace std;
// Function to maximize the cost
int pickup(vector >& grid)
{
int M = grid.size();
if (M == 0)
return 0;
int N = grid[0].size();
if (N == 0)
return 0;
vector > > dp(
M, vector >(
N, vector(N, INT_MIN)));
dp[0][0][N - 1] = grid[0][0]
+ grid[0][N - 1];
for (int i = 1; i < M; i++)
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
for (int l = a - 1; l
<= a + 1;
l++)
for (int r = b - 1;
r <= b + 1; r++) {
if (l < 0 || l >= N
|| r < 0
|| r >= N)
continue;
dp[i][a][b] = max(
dp[i][a][b],
((a != b)
? grid[i][a]
+ grid[i][b]
: grid[i][a])
+ dp[i - 1][l][r]);
}
for (int i =0 ;i > grid{
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
cout << pickup(grid);
return 0;
}
Python3
# Python code to implement the approach
# Function to maximize the cost
import sys
def pickup(grid):
M = len(grid)
if (M == 0):
return 0
N = len(grid[0])
if (N == 0):
return 0
dp = [[[(-sys.maxsize-1) for i in range(N)] for j in range(N)] for k in range(M)]
dp[0][0][N - 1] = grid[0][0] + grid[0][N - 1]
for i in range(1,M):
for a in range(N):
for b in range(N):
for l in range(a - 1,a + 2):
for r in range(b - 1,b + 2):
if (l < 0 or l >= N or r < 0 or r >= N):
continue
dp[i][a][b] = max(dp[i][a][b],(grid[i][a]+ grid[i][b] if (a != b) else grid[i][a]) + dp[i - 1][l][r])
ans = 0
for a in range(N):
for b in range(N):
ans = max(ans, dp[M - 1][a][b])
return ans
# Driver code
grid = [[ 3, 1, 1 ], [ 2, 5, 1 ],
[ 1, 5, 5 ], [ 2, 1, 1 ]]
print(pickup(grid))
# This code is contributed by shinjanpatra
C#
// C# code to implement the approach
using System;
class GFG
{
// Function to maximize the cost
static int pickup(int[,] grid)
{
int M = grid.GetLength(0);
if (M == 0)
return 0;
int N = grid.GetLength(1);
if (N == 0)
return 0;
int [,,]dp = new int[M,N,N];
dp[0,0,N - 1] = grid[0,0]
+ grid[0,N - 1];
for (int i = 1; i < M; i++)
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
for (int l = a - 1; l
<= a + 1;
l++)
for (int r = b - 1;
r <= b + 1; r++) {
if (l < 0 || l >= N
|| r < 0
|| r >= N)
continue;
dp[i,a,b] = Math.Max(
dp[i,a,b],
((a != b)
? grid[i,a]
+ grid[i,b]
: grid[i,a])
+ dp[i - 1,l,r]);
}
int ans = 0;
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
ans = Math.Max(ans, dp[M - 1,a,b]);
return ans;
}
// Driver code
public static void Main()
{
int[,] grid = {
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
Console.WriteLine( pickup(grid));
}}
// This code is contributed by ukasp.
Javascript
24
时间复杂度: O(M * N 2 )
辅助空间: O(M * N 2 )
实现 2:使用状态压缩
C++
// C++ code to implement the approach
#include
using namespace std;
// Function to maximize the cost
int pickup(vector >& grid)
{
int M = grid.size();
if (M == 0)
return 0;
int N = grid[0].size();
if (N == 0)
return 0;
vector > > dp(
2, vector >(
N, vector(N, INT_MIN)));
dp[0][0][N - 1] = grid[0][0]
+ grid[0][N - 1];
// Looping over all rows
for (int i = 1; i < M; i++)
// looping over every cell
// in the row for point1
for (int a = 0; a < N; a++)
// looping over every cell
// in the row for point2
for (int b = 0; b < N; b++)
// Capturing possible
// movements of point 1
for (int l = a - 1;
l <= a + 1; l++)
// Capturing possible
// movements of point2
for (int r = b - 1;
r <= b + 1; r++) {
if (l < 0 || l >= N
|| r < 0 || r >= N)
continue;
// Apply DP transition
dp[i % 2][a][b] = max(
dp[i % 2][a][b],
((a != b)
? grid[i][a]
+ grid[i][b]
: grid[i][a])
+ dp[(i - 1) % 2][l][r]);
}
// Loop over dp to get the final answer
int ans = 0;
for (int a = 0; a < N; a++)
for (int b = 0; b < N; b++)
ans = max(ans,
dp[(M - 1) % 2][a][b]);
return ans;
}
// Driver code
int main()
{
vector > grid{
{ 3, 1, 1 }, { 2, 5, 1 },
{ 1, 5, 5 }, { 2, 1, 1 }
};
cout << pickup(grid);
return 0;
}
24
时间复杂度: O(M * N 2 )
辅助空间: O(N 2 )