【经典专题】dp表的降维优化——滚动数组与强行滚动数组

问题引入

哪种情况下可以可以对dp表进行滚动数组优化?

直观的讲,在某一维度的跨度仅仅是1的时候(出现 i - 1 或者 j - 1 ,就可以直接删掉一维,即进行滚动数组优化。

滚动数组优化的代码怎么写?

由于滚动数组会让原先清晰的动态规划逻辑变的莫名其妙,因而最后不要想着一步到位、直接写低维dp。而是直接在原先代码的基础上删掉一维即可

可是,这种屡试不爽的思路每用10次翻车1次,究竟是为什么呢?

看来,上面这种“一维跨度”的思路并没有触及到滚动数组的本质,是一个错误的认知。下面,将用一个极为典型的例题,带你彻底弄懂滚动数组,与面试官侃侃而谈。

 
 

典型例题

【题目】最长公共子序列
【描述】给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。其中,子序列可以是原序列中不连续的元素。如果不存在公共子序列,返回 0 。

【输入】text1 = “abcde”, text2 = “ace”
【输出】3
【解释】最长公共子序列是 “ace” ,它的长度为 3 。

题目本身非常简单。这里直接给出代码。
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        char[] arr1 = text1.toCharArray();
        char[] arr2 = text2.toCharArray();

        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (arr1[i - 1] == arr2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
}

 
 

滚动数组(重点!

不难发现,状态转移方程的跨度都是1,是不是就说明我们可以无脑滚动数组了呢?

不妨在草稿纸上试一试:

在这里插入图片描述

我们突然意识到滚动数组的本质:

在不影响其他状态转移的前提下,进行状态值的覆盖,从而复用了数组空间。

而滚动数组安全性的前提是:

被覆盖的状态值是不会再被用到的;如果之后还会被用到,你却把它覆盖掉了,即“影响了其他状态转移”。

 
 

强行滚动(重点!

我们已经知道,本题的dp表在滚动时,会出现有效状态值的覆盖。所以我们就不能进行滚动数组优化了吗?

的确,不能再使用无脑的滚动数组优化。

但是,我们可以强行让它滚动起来。

如何实现?在有效状态值被覆盖之前,将它记忆下来。
 
下面的写法借助了一个临时的tmp,注意理解:

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        char[] arr1 = text1.toCharArray();
        char[] arr2 = text2.toCharArray();

        int[] dp = new int[n + 1];
        for (int i = 1; i <= m; i++) {
            int pre = 0;
            for (int j = 1; j <= n; j++) {
                int tmp = dp[j];
                if (arr1[i - 1] == arr2[j - 1]) {
                    dp[j] = pre + 1;
                } else {
                    dp[j] = Math.max(dp[j], dp[j - 1]);
                }
                pre = tmp;
            }
        }
        return dp[n];
    }
}

 
 

写在后面

虽然dp[][]的空间被压缩为dp[],看但时间复杂度丝毫不变————滚动数组只不过是一种"原地操作"罢了。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

E N D END END

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
通过空间优化版本的0-1背包问题解法,可以利用一维数组进行优化。这是因为在0-1背包问题中,每个物品只能选择放入背包一次或不放入,所以在计算第i个物品放入容量为j的背包的最大价值时,只需要考虑前i-1个物品放入容量为j的背包的最大价值和前i-1个物品放入容量为j-vol[i的背包的最大价值加上第i个物品的价值,取两者的较大值即可。通过这种方式,可以将二维数组转化为一维数组来实现空间优化。 具体的实现方法如下: 1. 创建一个一维数组dp,长度为背包的容量V+1,用于存储每个容量下的最大价值。 2. 遍历每个物品,从最后一个物品开始倒序遍历。 3. 对于当前遍历到的物品i,在容量为j的背包中,判断是否能够放入该物品: - 若当前容量j小于物品i的体积vol[i,则无法放入,最大价值保持不变,即dp[j = dp[j。 - 若当前容量j大于等于物品i的体积vol[i,则可以选择放入该物品或不放入该物品,取两者的较大值作为最大价值,即dp[j = max(dp[j], dp[j-vol[i]] + val[i])。 4. 完成遍历后,最终的最大价值即为dp[V。 这样,通过一维数组的空间优化,可以在0-1背包问题中节省空间,并且得到相同的最优解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [leetcode 背包类问题](https://blog.csdn.net/u014034683/article/details/114481074)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [0-1背包问题的一维数组优化解析](https://blog.csdn.net/hnjzsyjyj/article/details/126071689)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值