标签:记忆化搜索,动态规划
这道题和一道非常经典的动态规划题目机器人走方格类似,只是那个题目只能向下走和向右走,但本质和这道题是一样的,可以先去试着做一下这道题再来做跳跃这道题。
leetcode-62.不同路径
首先我们定义一个二维数组来表示从起点到当前位置的最大权值是多少
从以上定义可以发现,当前位置的最大权值就是当前位置的权值加上上一步可能的所有格子之中的权值最大的那个,只要我们反推上一步能走到哪些格子,就能找到上一步的最大值。
这里给出两种方法
采用递归自顶向下的方式求上一步最大权值是多少
使用备忘录记录已经访问过的格子的起点到当前格子的最大权值,避免重复计算
#include
using namespace std;
int memo[105][105];
int g[105][105];
int dire[9][2] = {{0, -1}, {0, -2}, {0, -3},{-1, 0}, {-1, -1}, {-1, -2},{-2, 0}, {-2, -1}, {-3, 0}};
int n, m;
int INF = 0x3f3f3f3f;
int dfs(int i, int j) {
if (memo[i][j] != -INF) return memo[i][j];
int cur = g[i][j]; // 当前方格的权值
for (auto [dx, dy] : dire) {
int nx = i + dx, ny = j + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
memo[i][j] = max(memo[i][j], dfs(nx, ny));
}
}
memo[i][j] += cur;
return memo[i][j];
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) {
cin >> g[i][j];
memo[i][j] = -INF;
}
memo[0][0] = g[0][0]; // 初始化
cout << dfs(n - 1, m - 1);
return 0;
}
from functools import lru_cache
dire = [[0, -1], [0, -2], [0, -3], [-1, 0], [-1, -1], [-1, -2], [-2, 0], [-2, -1], [-3, 0]]
n, m = map(int, input().split(" "))
g = []
for i in range(n):
g.append(list(map(int, input().split())))
@lru_cache(None)
def dfs(i, j):
if i == 0 and j == 0: return g[0][0]
ans = -math.inf
cur = g[i][j]
for dx, dy in dire:
nx, ny = i + dx, j + dy
if 0 <= nx < n and 0 <= ny < m:
ans = max(ans, dfs(nx, ny))
return ans + cur
print(dfs(n - 1, m - 1))
import java.util.Scanner;
public class Main {
static int[][] g;
static int[][] memo;
static int INF = Integer.MAX_VALUE / 2;
static int n, m;
static int[][] dire = {{0, -1}, {0, -2}, {0, -3}, {-1, 0}, {-1, -1}, {-1, -2}, {-2, 0}, {-2, -1}, {-3, 0}};
static int dfs(int i, int j) {
if (memo[i][j] != -INF) return memo[i][j];
int cur = g[i][j];
for (int[] d : dire) {
int dx = d[0], dy = d[1];
int nx = i + dx, ny = j + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
memo[i][j] = Math.max(memo[i][j], dfs(nx, ny));
}
}
memo[i][j] += cur;
return memo[i][j];
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
m = scan.nextInt();
g = new int[n][m];
memo = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
g[i][j] = scan.nextInt();
memo[i][j] = -INF;
}
}
memo[0][0] = g[0][0];
System.out.println(dfs(n - 1, m - 1));
scan.close();
}
}
使用自底向上的方式从起点开始递推
dp数组定义:还是起点到当前格子的最大权值之和
状态转移方程:从上一步的几个格子的起点到各自的最大权值之和的最大值加上当前格子的权值
初始化:dp[0][0] = g[0][0],最左上角的格子没有上一步
#include
using namespace std;
int dp[105][105];
int g[105][105];
int dire[9][2] = {{0, -1}, {0, -2}, {0, -3}, {-1, 0}, {-1, -1}, {-1, -2}, {-2, 0}, {-2, -1}, {-3, 0}};
int n, m;
int INF = 0x3f3f3f3f;
int main()
{
cin >> n >> m;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) {
cin >> g[i][j];
dp[i][j] = -INF;
}
dp[0][0] = g[0][0]; // 初始化
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (i == 0 && j == 0) continue;
for (auto [dx, dy] : dire) { // 找上一步的最大值
int nx = i + dx, ny = j + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
dp[i][j] = max(dp[i][j], dp[nx][ny]);
}
}
dp[i][j] += g[i][j];
}
}
cout << dp[n - 1][m - 1];
return 0;
}
import math
dire = [[0, -1], [0, -2], [0, -3], [-1, 0], [-1, -1], [-1, -2], [-2, 0], [-2, -1], [-3, 0]]
n, m = map(int, input().split(" "))
dp = [[-math.inf] * m for _ in range(n)]
g = []
for i in range(n):
g.append(list(map(int, input().split())))
dp[0][0] = g[0][0]
for i in range(n):
for j in range(m):
if i == 0 and j == 0: continue
for dx, dy in dire:
nx, ny = i + dx, j + dy
if 0 <= nx < n and 0 <= ny < m:
dp[i][j] = max(dp[i][j], dp[nx][ny])
dp[i][j] += g[i][j]
print(dp[n - 1][m - 1])
import java.util.Scanner;
public class Main {
static int INF = Integer.MAX_VALUE / 2;
static int[][] dire = {{0, -1}, {0, -2}, {0, -3}, {-1, 0}, {-1, -1}, {-1, -2}, {-2, 0}, {-2, -1}, {-3, 0}};
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt(), m = scan.nextInt();
int[][] g = new int[n][m];
int[][] dp = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
g[i][j] = scan.nextInt();
dp[i][j] = -INF;
}
}
dp[0][0] = g[0][0];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i == 0 && j == 0) continue;
for (int[] d : dire) {
int dx = d[0], dy = d[1];
int nx = i + dx, ny = j + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < m) {
dp[i][j] = Math.max(dp[i][j],dp[nx][ny]);
}
}
dp[i][j] += g[i][j];
}
}
System.out.println(dp[n - 1][m - 1]);
scan.close();
}
}