「程序员必须掌握的算法」动态规划「中篇」

简介: 「程序员必须掌握的算法」动态规划「中篇」

「程序员必须掌握的算法」动态规划「中篇」

在程序员的日常工作中,掌握各种算法是必不可少的。其中动态规划是常用的一种算法,在解决优化问题方面有着广泛的应用。本文主要介绍动态规划的中等难度内容,包括二维DP和数位DP。

二维DP

二维动态规划(DP)是指,用一个二维数组来表示状态,其中第一维表示选取到哪个元素,第二维表示当前选取的状态。这种方法在解决问题中,需要转移的状态比较复杂的情况下特别有用。

例题1:矩阵路径计数

题目描述:给定一个 n × m n \times mn×m 的矩阵,从左上角走到右下角,一共有多少种走法。其中每次只能向右或向下走。

思路分析:定义状态 d p i , j dp_{i,j}dpi,j 表示从 ( 1 , 1 ) (1,1)(1,1) 走到 ( i , j ) (i,j)(i,j) 的方案数,那么状态转移方程就可以定义为:d p i , j = d p i − 1 , j + d p i , j − 1 dp_{i,j} = dp_{i-1,j} + dp_{i,j-1}dpi,j=dpi1,j+dpi,j1,其中 d p i − 1 , j dp_{i-1,j}dpi1,j 表示从上面的位置走到 ( i , j ) (i,j)(i,j)d p i , j − 1 dp_{i,j-1}dpi,j1 表示从左边的位置走到 ( i , j ) (i,j)(i,j),两种情况的方案数相加即可得到从 ( 1 , 1 ) (1,1)(1,1) 走到 ( i , j ) (i,j)(i,j) 的方案数。特别地,当 i = 1 i=1i=1j = 1 j=1j=1 时,只有一条路径,因此 d p 1 , j = d p i , 1 = 1 dp_{1,j}=dp_{i,1}=1dp1,j=dpi,1=1

最后,返回 d p n , m dp_{n,m}dpn,m 就是从左上角走到右下角的方案数。

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

数位DP

数位DP是指,用一个数字的各个位数作为状态,来解决数字相关问题的一种算法。数位DP的思路是把数字拆开,按照位数逐位处理,求解每一位的方案数,最后组合得到最终的结果。

例题2:数字和为 k kk 的数的个数

题目描述:给定两个正整数 a aab bb,求满足 a ≤ x ≤ b a \leq x \leq baxb,并且 x xx 的所有数字之和等于 k kk 的自然数的个数。

思路分析:我们可以用数位DP的方法来解决这个问题。和前面的例题一样,我们定义状态 d p i , j , k dp_{i,j,k}dpi,j,k 表示当前处理到第 i ii 位,数字和为 j jj 的方案数。那么状态转移方程可以定义为:d p i , j , k = ∑ d = 0 9 d p i − 1 , j − d , k − 1 dp_{i,j,k} = \sum \limits _{d=0} ^9 dp_{i-1,j-d,k-1}dpi,j,k=d=09dpi1,jd,k1,其中 d dd 表示当前处理到的数字,也就是第 i ii 位的数字。那么 d p i − 1 , j − d , k − d dp_{i-1,j-d,k-d}dpi1,jd,kd 就表示前 i − 1 i-1i1 位数字和为 j − d j-djd,数字个数为 k − 1 k-1k1 的方案数,然后把所有 d dd 的情况都累加到一起即可得到 d p i , j , k dp_{i,j,k}dpi,j,k 的值。特别地,当 i = 0 i=0i=0j = k = 0 j=k=0j=k=0 时,可以将此视为一个合法方案,即 d p 0 , 0 , 0 = 1 dp_{0,0,0}=1dp0,0,0=1

下面,我们对数位DP的状态进行优化,因为对于一个数字 n nn,它的各个数位的数字之和最大为 9 99,因此 d p i , j , k = 0 dp_{i,j,k}=0dpi,j,k=0j > 9 k j>9kj>9k,即当前处理到第 i ii 位,数字和大于 9 k 9k9k 时可以直接跳过。另外,我们为了方便计算,可以将上限转化成字符串,比如将 b = 1234 b=1234b=1234 转化为 s = " 1234 " s="1234"s="1234",然后逐位考虑,如果 s i < d s_i<dsi<d,那么当前数字不能取 d dd,下一位需要继续考虑,如果 s i = d s_i=dsi=d,那么当前数字可以取 d dd,下一位也需要继续考虑,如果 s i > d s_i>dsi>d,那么下一位可以任意填充数字,无需继续考虑。

在实现的时候,我们需要用一个 s u m sumsum 变量来记录当前数字的各个位数之和,把所有状态的方案数相加,得到最终的结果。

public int count(int n, int k) {
    int[][][] dp = new int[11][91][91];
    dp[0][0][0] = 1;
    for (int i = 1; i <= 10; i++) {
        for (int j = 0; j <= 81; j++) {
            for (int l = 0; l <= 81; l++) {
                for (int d = 0; d <= 9; d++) {
                    if (j >= d && l >= d) {
                        dp[i][j][l] += dp[i - 1][j - d][l - d];
                    }
                }
            }
        }
    }
    int ans = 0;
    String s = String.valueOf(n);
    int[] num = new int[s.length() + 1];
    for (int i = 0; i < s.length(); i++) {
        num[i + 1] = s.charAt(i) - '0';
    }
    for (int i = 1; i < num.length; i++) {
        for (int j = 0; j < num[i]; j++) {
            if (j <= k - ans - (num.length - i) * 9) {
                ans += dp[num.length - i][k - ans - j][(num.length - i) * 9 + j];
            }
        }
        if (num[i] > k - ans - (num.length - i) * 9) {
            break;
        }
        sum += num[i];
    }
    if (sum == k) {
        ans++;
    }
    return ans;
}

结语

本文主要介绍了动态规划的中等难度内容,包括二维DP和数位DP,并附有两道例题作为模版参考。通过实际运用和练习,相信读者可以更加深入地理解动态规划的思想和应用。

相关文章
|
3天前
|
算法 Java 机器人
Java数据结构与算法:动态规划之斐波那契数列
Java数据结构与算法:动态规划之斐波那契数列
|
20天前
|
算法 数据挖掘 开发者
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
|
18天前
|
存储 算法
数据结构与算法之动态规划--旷工问题
数据结构与算法之动态规划--旷工问题
30 1
|
3天前
|
算法 JavaScript 程序员
程序员必知:《程序设计与算法(二)算法基础》《第一周枚举》熄灯问题POJ
程序员必知:《程序设计与算法(二)算法基础》《第一周枚举》熄灯问题POJ
|
6天前
|
算法 Java 决策智能
Java数据结构与算法:动态规划之背包问题
Java数据结构与算法:动态规划之背包问题
|
9天前
|
存储 缓存 算法
五大常见算法策略之——动态规划策略(Dynamic Programming)
五大常见算法策略之——动态规划策略(Dynamic Programming)
|
16天前
|
人工智能 算法
计算机算法设计与分析 第3章 动态规划 (笔记)
计算机算法设计与分析 第3章 动态规划 (笔记)
|
20天前
|
机器学习/深度学习 存储 算法
LeetCode题目 90:五种算法 回溯\迭代\位掩码\字典树\动态规划实现 子集ll
LeetCode题目 90:五种算法 回溯\迭代\位掩码\字典树\动态规划实现 子集ll
|
20天前
|
SQL 算法 数据可视化
python 贪心算法 动态规划实现 跳跃游戏ll【力扣题45】
python 贪心算法 动态规划实现 跳跃游戏ll【力扣题45】
|
20天前
|
存储 SQL 算法
解锁动态规划:从斐波那契到高效算法
解锁动态规划:从斐波那契到高效算法