刷题备战蓝桥杯之跳跃(经典动态规划)

跳跃

  • 题目描述
  • 整体思路
  • 方法一. 记忆化搜索
    • 思路
    • 代码
      • c++
      • python
      • java
  • 方法二. 动态规划
    • 思路
    • 代码
      • c++
      • python
      • java

标签:记忆化搜索,动态规划

题目描述

刷题备战蓝桥杯之跳跃(经典动态规划)_第1张图片

整体思路

这道题和一道非常经典的动态规划题目机器人走方格类似,只是那个题目只能向下走和向右走,但本质和这道题是一样的,可以先去试着做一下这道题再来做跳跃这道题。
leetcode-62.不同路径

首先我们定义一个二维数组来表示从起点到当前位置的最大权值是多少
从以上定义可以发现,当前位置的最大权值就是当前位置的权值加上上一步可能的所有格子之中的权值最大的那个,只要我们反推上一步能走到哪些格子,就能找到上一步的最大值。

这里给出两种方法

方法一. 记忆化搜索

思路

采用递归自顶向下的方式求上一步最大权值是多少
使用备忘录记录已经访问过的格子的起点到当前格子的最大权值,避免重复计算

代码

c++

#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;
}

python

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))

java

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],最左上角的格子没有上一步

代码

c++

#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;
}

python

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])

java

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();
    }
}

你可能感兴趣的:(java,python,c++,算法,动态规划)