记忆化搜索

143 阅读1分钟

记忆化搜索

滑雪

  • 问题背景

Snipaste_2023-03-28_20-17-48.png

  • Dp分析

Snipaste_2023-03-28_20-18-36.png

  • 状态表示

    • 二维状态表示f(i,j)
    • f(i,j)表示的是哪一个集合:所有满足如下条件的集合
      • 所有从f(i,j)开始滑的路径
    • f(i,j)存的是什么属性:MaxMin数量;在这里f(i,j)存的应该是Max,即从f(i,j)开始滑,可以划过路径的最大值
  • 状态计算:f(i,j)可以怎么算出来?

    • 分成四个子集
      • 向上滑
        • f(i, j) = f(i - 1, j) + 1
      • 向右滑
        • f(i, j) = f(i, j + 1) + 1
      • 向下滑
        • f(i, j) = f(i + 1, j) + 1
      • 向左滑
        • f(i, j) = f(i, j - 1) + 1
      • 这里将向四个方向滑后的坐标记为 (a, b)
        • (a, b) 需要满足 a >= 1 && a <= n && b >= 1 && b <= m && g(a, b) < g(i, j)
  • 代码

    • public static int dp(int x, int y) {
          //如果f[x][y]已经被算出来了,直接返回
          if (f[x][y] != -1) {
              return f[x][y];
          }
          //如果f[i][j]没有被算过,赋初值1
          f[x][y] = 1;
          //遍历四个方向
          for (int i = 0; i < 4; i++) {
              int a = x + dx[i];
              int b = y + dy[i];
              if (a >= 1 && a <= n && b >= 1 && b <= m && g[x][y] > g[a][b]) {
                  f[x][y] = Math.max(f[x][y], dp(a, b) + 1);
              }
          }
          return f[x][y];
      }
      

练习

01 滑雪

  • 题目

Snipaste_2023-03-28_20-53-37.png

  • 题解
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 310;
    public static int[][] g = new int[N][N];
    //从f(i,j)开始滑,可以划过路径的最大值
    public static int[][] f = new int[N][N];
    public static int[] dx = new int[]{-1, 0, 1, 0};
    public static int[] dy = new int[]{0, 1, 0, -1};
    public static int n, m;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        String[] str1 = br.readLine().split(" ");
        n = Integer.parseInt(str1[0]);
        m = Integer.parseInt(str1[1]);
        for (int i = 1; i <= n; i++) {
            String[] str2 = br.readLine().split(" ");
            for (int j = 1; j <= m; j++) {
                g[i][j] = Integer.parseInt(str2[j - 1]);
            }
        }

        //初始化
        for (int i = 1; i <= n; i++) {
            Arrays.fill(f[i], 1, m + 1, -1);
        }

        int res = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                //这里是dp(i, j)而不是f[i][j]是因为f[i][j]还没有算出来
                //f[i][j]是在dp(i, j)中才被算出来的
                res = Math.max(res, dp(i, j));
            }
        }
        pw.println(res);
        pw.close();
        br.close();
    }

    public static int dp(int x, int y) {
        //如果f[x][y]已经被算出来了,直接返回
        if (f[x][y] != -1) {
            return f[x][y];
        }

        //如果f[i][j]没有被算过,赋初值1
        f[x][y] = 1;
        //遍历四个方向
        for (int i = 0; i < 4; i++) {
            int a = x + dx[i];
            int b = y + dy[i];
            if (a >= 1 && a <= n && b >= 1 && b <= m && g[x][y] > g[a][b]) {
                f[x][y] = Math.max(f[x][y], dp(a, b) + 1);
            }
        }
        return f[x][y];
    }
}