Algorithm Notes
  • Introduction
  • Search & Backtracking 搜索与回溯
    • Tree 与 BackTracking 的比较
    • Subsets, Combination 与 Permutation
    • Subsets & Combinations & Combination Sum
    • 枚举法
    • N 皇后 + 矩阵 Index Trick
    • Sudoku 数独 + 矩阵 Index Trick
    • Word Ladder I & II
    • Number of ways 类
    • DFS flood filling
    • Strobogrammatic 数生成
    • String 构造式 DFS + Backtracking
    • Word Pattern I & II
    • (G) Binary Watch
    • (FB) Phone Letter Combination
    • 常见搜索问题的迭代解法
  • String,字符串类
    • 多步翻转法
    • Substring 结构和遍历
    • Palindrome 问题
    • Palindrome Continued
    • String / LinkedList 大数运算
    • 序列化与压缩
    • 5/24 String 杂题
    • Knuth–Morris–Pratt 字符串匹配
    • Lempel–Ziv–Welch 字符串压缩算法
    • (G) Decode String
    • (G) UTF-8 Validation
  • Binary Tree,二叉树
    • 各种 Binary Tree 定义
    • LCA 类问题
    • 三序遍历,vertical order
    • Post order traversal 的应用
    • Min/Max/Balanced Depth
    • BST
    • 子树结构
    • Level Order traversal
    • Morris 遍历
    • 修改结构
    • 创建 / 序列化
    • 子树组合,BST query
    • 路径与路径和
    • NestedInteger 类
    • (FB) 从 Binary Tree Path 看如何递归转迭代
    • (FB) Binary Tree Path 比较路径大小
    • 比较好玩的 Binary Tree 概率题
  • Segment & Fenwick Tree,区间树
    • Segment Tree 基础操作
    • Segment Tree 的应用
    • Fenwick Tree (Binary Indexed Tree)
    • Range Sum Query 2D - Immutable
  • Union-Find,并查集
    • Union-Find,并查集基础
    • Union-Find, 并查集应用
  • Dynamic Programming, 动态规划
    • 6/20, 入门 House Robber
    • 7/12, Paint Fence / House
    • 6/24, 滚动数组
    • 6/24, 记忆化搜索
    • 6/24, 博弈类 DP
    • 博弈类DP, Flip Game
    • 6/25, 区间类DP
    • 6/27, subarray 划分类,股票
    • 7/2, 字符串类
    • Bomb Enemies
    • 8/2,背包问题
    • (G) Max Vacation
    • (11/4新增) AST 子树结构 DP
  • LinkedList,链表
    • 6/9, LinkedList,反转与删除
    • 6/11, LinkedList 杂题
    • (FB) 链表的递归与倒序打印
  • LinkedIn 面经,算法题
    • 6/17, LinkedIn 面经题
    • 6/28, LinkedIn 面经题
    • 7/6, LinkedIn 面经
    • Shortest Word Distance 类
    • DFA Parse Integer
  • Two Pointers,双指针
    • 3 Sum, 3 Sum Closest / Smaller, 4 Sum
    • 对撞型,灌水类
    • 对撞型,partition类
    • Wiggle Sort I & II
    • 双指针,窗口类
    • 双指针,窗口类
    • Heap,排序 matrix 中的 two pointers
  • Bit & Math,位运算与数学
    • Bit Manipulation,对于 '1' 位的操作
    • Math & Bit Manipulation, Power of X
    • 坐标系 & 数值计算类
    • Add Digits
    • 用 int 做字符串 signature
  • Interval 与 扫描线
    • Range Addition & LCS
    • 7/5, Interval 类,扫描线
  • Trie,字典树
    • 6/9, Trie, 字典树
  • 单调栈,LIS
    • 4/13 LIS
    • 栈, 单调栈
    • Largest Divisible Subset
  • Binary Search 类
    • Matrix Binary Search
    • Array Binary Search
    • Find Peak Element I & II
    • **Median of Two Sorted Arrays
  • Graph & Topological Sort,图 & 拓扑排序
    • 有向 / 无向 图的基本性质和操作
    • 拓扑排序, DFS 做法
    • 拓扑排序, BFS 做法
    • Course Schedule I & II
    • Alien Dictionary
    • Undirected Graph, BFS
    • Undirected Graph, DFS
    • 矩阵,BFS 最短距离探索
    • 欧拉回路,Hierholzer算法
    • AI, 迷宫生成
    • AI, 迷宫寻路算法
    • (G) Deep Copy 无向图成有向图
  • 括号与数学表达式的计算
  • Iterator 类
  • Majority Element,Moore's Voting
  • Matrix Inplace Operations
  • 常见数据结构设计
  • (G) Design / OOD 类算法题
  • 随机算法 & 数据结构
  • (FB) I/O Buffer
  • (FB) Simplify Path, H-Index I & II
  • (FB) Excel Sheet, Remove Duplicates
  • Integer 的构造,操作,序列化
  • Frequency 类问题
  • Missing Number 类,元素交换,数组环形跳转
  • 8/10, Google Tag
  • (FB) Rearrange String k Distance Apart
  • Abstract Algebra
    • Chap1 -- Why Abstract Algebra ?
    • Chap2 -- Operations
    • Chap3 -- The Definition of Groups
Powered by GitBook
On this page
  • Unique Paths II
  • Minimum Path Sum
  • Maximal Square
  • 对于每一个位置 (i,j):
  • 因此,以(i,j) 为右下角的最大正方形边长,取决于以上三个点。Optimal substructure 确定。
  • 循环初始化的细节要注意下,此外还要注意取mod的时候 % 是在最后,而不是 (i % 2) - 1
  • Maximal Square Follow Up
  • 01矩阵里面寻找一个对角线为 1, 其他全为 0 的矩阵
  • Maximal Rectangle

Was this helpful?

  1. Dynamic Programming, 动态规划

6/24, 滚动数组

Previous7/12, Paint Fence / HouseNext6/24, 记忆化搜索

Last updated 4 years ago

Was this helpful?

潜水归来。刷起吧。

待刷类别: 记忆化搜索DP,博弈类DP,区间类DP,背包类DP,划分类DP

挺经典的 2D-array 上做 DP 问题。

public class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if (obstacleGrid == null || obstacleGrid.length == 0) return 0;

        int m = obstacleGrid.length, n = obstacleGrid[0].length;
        int[] dp = new int[n + 1];
        dp[1] = 1;

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (obstacleGrid[i - 1][j - 1] == 1) dp[j] = 0;
                else dp[j] = dp[j] + dp[j - 1];
            }
        }
        return dp[n];
    }
}

思路一样,都属于 2D-array 上的路径问题。考虑到没有负数,也不存在往回走的情况,因此这个问题的 optimal substructure 比较简单,只依赖于来路的两个位置。

public class Solution {
    public int minPathSum(int[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;

        for(int i = 1; i < cols; i++){
            grid[0][i] += grid[0][i - 1];
        }
        for(int i = 1; i < rows; i++){
            grid[i][0] += grid[i - 1][0];
        }

        for(int i = 1; i < rows; i ++){
            for(int j = 1; j < cols; j++){
                grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
            }
        }

        return grid[rows - 1][cols - 1];
    }
}

一年前不熟悉动态规划去硬想的时候,困扰了我有段时间的题。

首先不难判断出,这是一个在给定 2D-array 上直接做 DP 的问题,有天然的 overlap subproblems,因而重点在于 states 之间的关联和构造,寻找 optimal substructure.

正方形在 2D-array 上的定位相对简单,只需要有一个顶点,加上边长就可以,其中对于所有正方形,顶点位置固定。既然矩阵 DP 大多从左上向右下扫,用正方形右下角定位比较简便。

对于每一个位置 (i,j):

  • 其边长向左扩展受限于 (i, j-1);

  • 其边长向上扩展受限于 (i-1, j);

  • 其边长向对角线扩展受限于(i-1, j-1);

因此,以(i,j) 为右下角的最大正方形边长,取决于以上三个点。Optimal substructure 确定。

剩下的优化就是滚动数组了,我们只需要保存前面一行以及当前行的左面,因此 int[2][cols].

循环初始化的细节要注意下,此外还要注意取mod的时候 % 是在最后,而不是 (i % 2) - 1

public class Solution {
    public int maximalSquare(char[][] matrix) {
        if(matrix == null || matrix.length == 0) return 0;
        int rows = matrix.length;
        int cols = matrix[0].length;
        int max = 0;

        int[][] dp = new int[2][cols];

        for(int i = 0; i < cols; i++){
            dp[0][i] = matrix[0][i] - '0';
            max = Math.max(dp[0][i], max);
        }

        for(int i = 1; i < rows; i++){
            dp[i % 2][0] = matrix[i][0] - '0';
            for(int j = 1; j < cols; j++){
                if(matrix[i][j] == '0'){
                    dp[i % 2][j] = 0;
                } else {
                    dp[i % 2][j] = Math.min(dp[i % 2][j - 1],
                                   Math.min(dp[(i - 1) % 2][j], 
                                            dp[(i - 1) % 2][j - 1])) + 1;
                }
                max = Math.max(dp[i % 2][j], max);
            }
        }

        return max * max;
    }
}

Maximal Square Follow Up

01矩阵里面寻找一个对角线为 1, 其他全为 0 的矩阵

这题 LintCode 上还没有,所以就自己开 IDE 写,自己测 test case 了。思路和上一题类似。

由于连续对角线构造的依然是正方形,我们可以以一样的状态定义和转移方程来定义 (i,j) = 以 (i, j) 为起点的最大 1 对角线正方形。

对于(i , j),我们可以通过(i - 1, j - 1) 来判断在对角线方向可以延伸多长;然而同时也要保证在 (i, j) 左面的宽为1的矩形全部是 0,上面宽为1的矩形也全部是0,其长度至少要等于 (i - 1, j - 1) 上的对角线长度。如果条件都满足,则可以继续 extend.

因此从 optimal substructure 来讲,和 Maximal Square 非常相似,都是从【左】【上】【对角线】来寻找能 valid 延伸的最长距离,其中最短的那个作为当前位置的 bottleneck.

我们需要构造额外的两个 DP 矩阵,用于记录对于每个位置 (i, j) 能向上和向左延伸的最长连续 0 长度。 O(mn) 时间, O(mn) 空间。

leftLen 和 upLen 也可以用滚动数组优化,不过貌似只能优化其中一个。

public class Main {

    private static int maximalDiagnoal(int[][] matrix){
        if(matrix == null || matrix.length == 0) return 0;
        int rows = matrix.length;
        int cols = matrix[0].length;

        int[][] leftLen = new int[rows][cols];
        int[][] upLen = new int[rows][cols];

        int max = 0;

        for(int i = 0; i < cols; i++){
            if(matrix[0][i] == 0) upLen[0][i] = 1;
            max = Math.max(max, matrix[0][i]);
        }
        for(int i = 0; i < rows; i++){
            if(matrix[i][0] == 0) leftLen[i][0] = 1;
            max = Math.max(max, matrix[i][0]);
        }

        for(int i = 1; i < rows; i++){
            for(int j = 1; j < cols; j++){
                if(matrix[i][j] == 0){
                    leftLen[i][j] = leftLen[i][j-1] + 1;
                    upLen[i][j] = upLen[i-1][j] + 1;
                }
                max = Math.max(max, matrix[i][j]);
                // By default 0
            }
        }

        int[][] dp = new int[2][cols];

        for(int i = 0; i < cols; i++){
            dp[0][i] = matrix[0][i];
        }

        for(int i = 1; i < rows; i++){
            dp[i % 2][0] = matrix[i][0];
            for(int j = 1; j < cols; j++){
                if(matrix[i][j] == 1){
                    int diag = dp[(i-1) % 2][j-1];
                    int left = leftLen[i][j-1];
                    int up = upLen[i-1][j];

                    int min = Math.min(diag, Math.min(left, up));

                    dp[i % 2][j] = min + 1;
                    if(min + 1 > max){
                        max = min + 1;
                        System.out.println("Row : " + i + "  Col : " + j);
                        System.out.println("Current Max : " + (min + 1));
                    }
                }
            }
        }

        return max;
    }

    public static void main(String[] args){

        int[][] matrix1 =  {{0,1,1,0,0,1},
                            {1,0,0,1,0,1},
                            {0,1,1,0,0,1},
                            {1,1,0,1,0,1},
                            {1,0,0,0,1,0}};

        int[][] matrix2 =  {{1,0,0,0,0,1},
                            {0,1,0,0,0,1},
                            {0,0,1,0,0,1},
                            {0,0,0,1,0,1},
                            {0,0,0,0,1,0}};

        int[][] matrix3 =  {{1,0,0,0,0,1},
                            {0,1,0,0,0,1},
                            {0,0,1,0,0,1},
                            {0,0,0,1,0,1},
                            {0,0,1,0,1,0}};
        testRun(matrix1);
        testRun(matrix2);
        testRun(matrix3);

    }

    private static void testRun(int[][] matrix){
        for(int i = 0; i < matrix.length; i++){
            for(int j = 0; j < matrix[0].length; j++){
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }

        System.out.println("Max square with 1's only at diagnal : " + maximalDiagnoal(matrix));
    }
}

这题显然就比 Maximal Square 复杂了,因为只有一个边长无法确定矩形位置,更无法确定面积。因为多了一个变量,所以我们的 DP 要多加一个维度,dp[i][j][k] = 以(i, j) 为右下角,底边长度为 k 的最大矩形高度。时间复杂度为 O(n^3)

我不太建议也不太觉得面试会考这种三维的 DP... 这题其实更适合用 maximal histogram 的思路去做,等我之后再回来搞定这题。

Unique Paths II
Minimum Path Sum
Maximal Square
Maximal Rectangle