线性DP - 数字三角形

121 阅读1分钟
  • 问题背景

Snipaste_2023-03-22_22-44-26.png

  • Dp分析

Snipaste_2023-03-22_22-47-32.png

  • 状态表示

    • 二维状态表示f(i,j)
    • f(i,j)表示的是哪一个集合:所有满足如下条件的集合
      • 所有从起点出发,走到 (i, j) 的路径
    • f(i,j)存的是什么属性:MaxMin数量;在这里f(i,j)存的应该是最大值,即路径上数字和的最大值 * 状态计算:f(i,j)可以怎么算出来?
    • 这里可以将集合分为两个子集
      • 来自左上
        • f(i, j) = f(i - 1, j - 1) + a(i, j)
      • 来自右上
        • f(i, j) = f(i - 1, j) + a(i, j)
    • 因此 f(i, j) = max( f(i - 1, j - 1), f(i - 1, j) ) + a(i, j)
  • 代码

    • f[1][1] = a[1][1];
      for (int i = 2; i <= n; i++) {
          for (int j = 1; j <= n; j++) {
              f[i][j] = Math.max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];
          }
      }
      
  • 优化:将二维转换成一维

    • 观察状态计算的过程,可以发现两个特点:

      • f( i ) 层的计算只用到了f(i - 1)
      • f( i ) 层中的 j 的计算只用到了**f(i - 1)**层中的 jj - 1
    • 因此可以做如下优化

      • i 这一维直接优化掉,i 的每一层循环中的 j 都在原先的 j 上进行覆盖
      • j 的每一层循环中要注意 ,在计算 j 时要用到当前上一层 jj - 1 ,由于j - 1 < j,所以在这一层循环中要从后往前遍历,防止 j - 1 被提前更新
    • 代码

      • f[1] = a[1][1];
        for (int i = 2; i <= n; i++) {
            for (int j = n; j >= 1; j--) {
                f[j] = Math.max(f[j - 1], f[j]) + a[i][j];
            }
        }
        

练习

  • 题目

Snipaste_2023-03-22_23-42-36.png

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

public class Main {
    public static final int N = 510;
    public static final int INF = 0x3f3f3f3f;
    public static int[][] a = new int[N][N];
    public static int[][] f = new int[N][N];
    public static int n;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        n = Integer.parseInt(br.readLine());
        a[1][1] = Integer.parseInt(br.readLine());
        //i表示层数
        for (int i = 2; i <= n; i++) {
            String[] str1 = br.readLine().split(" ");
            for (int j = 1; j <= i; j++) {
                a[i][j] = Integer.parseInt(str1[j - 1]);
            }
        }
        //注意处理边界,自己模拟一下
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= n; j++) {
                f[i][j] = -INF;
            }
        }

        f[1][1] = a[1][1];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                f[i][j] = Math.max(f[i - 1][j - 1], f[i - 1][j]) + a[i][j];
            }
        }
        int res = -INF;
        for (int i = 1; i <= n; i++) {
            res = Math.max(res, f[n][i]);
        }
        pw.println(res);
        br.close();
        pw.close();
    }
}
  • 题解2
import java.io.*;
import java.util.*;

public class Main {
    public static final int N = 510;
    public static final int INF = 0x3f3f3f3f;
    public static int[][] a = new int[N][N];
    public static int[] f = new int[N];
    public static int[] g = new int[N];
    public static int n;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
        n = Integer.parseInt(br.readLine());
        a[1][1] = Integer.parseInt(br.readLine());
        //i表示层数
        for (int i = 2; i <= n; i++) {
            String[] str1 = br.readLine().split(" ");
            for (int j = 1; j <= i; j++) {
                a[i][j] = Integer.parseInt(str1[j - 1]);
            }
        }
        //注意处理边界,自己模拟一下
        for (int i = 0; i <= n; i++) {
            f[i] = -INF;
        }

        f[1] = a[1][1];
        for (int i = 2; i <= n; i++) {
            for (int j = n; j >= 1; j--) {
                f[j] = Math.max(f[j - 1], f[j]) + a[i][j];
            }
        }
        int res = -INF;
        for (int i = 1; i <= n; i++) {
            res = Math.max(res, f[i]);
        }
        pw.println(res);
        br.close();
        pw.close();
    }
}