轻松学DP——动态规划 + 0-1背包

动态规划算法

动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法。

动态规划算法与分治算法类似。其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到下一个更大的子问题解,这样一步一步扩大条件,直到达到原问题的条件,也就得到了原问题的解。

动态规划问题一般都是将问题描述成一个一维数组或者二维数组的方式,把目标的位置放在这个数组的最后一个位置。通过相当于填表的方式,一步一步扩大这个表格,直到从第一个位置走到最后一个位置,最后一个位置的状态就是这个问题的解。

动态规划具备了以下三个特点:

  1. 把原来的问题分解成了几个相似的子问题
  2. 所有的子问题都只需要解决一次
  3. 储存子问题的解。(提供给大问题用的)

动态规划的本质,是对问题状态的定义状态转移方程的定义。(状态以及状态之间的递推关系)

动态规划问题一般从以下四个角度考虑:

  1. 状态定义
  2. 状态间的转移方程定义
  3. 状态的初始化
  4. 返回结果

状态的定义要求:定义的状态一定要形成递推关系, 这样才可以通过小的问题的解去推大问题的解嘛

适用场景:最大值/最小值, 可不可行, 是不是,方案个数

手把手练习

LeetCode139_字符串分割

在这里插入图片描述

怎么用动态规划算法解决这个问题,首先我们先想这个思想~

题目问你 “可不可以” 用词典的小字符串拼接成我们的大字符串,上面我们概括了动态规划的使用场景,这里这个 “可不可以” … 符合动态规划的场景!

然后我们再想:目前的状态是我们有一个字符串 s ,要找他从字典里被成功分词的子串, 那,我们来想象一下:定义一个 i:他为 s 串的长度,定义一个 j:我们让 j < i;j>0,定义一个F(i) : i 长度串的状态,如果为 true,表示能被词典分割,为 false 则不能。

那么如果: F(j) == true && substring(j,i) 在词典中可以找到。也就是说,在 [0,i] 原本这个子串大小中,有一个长度小于 i 大小的下标为 j 的子串,这个子串的目前状态是能被词典分割,那么,如果 [j, i] 这一段小字符串也能在词典中找到,是不是就说明:F(i) = true。????

顿悟了吗老铁,觉得能理解的点个赞呗~

话不多说上代码:要看一下代码里面的注解内容奥~ 每一个问题我们都尝试根据四个特点写出来状态递推

import java.util.List;
/*
    题目描述
    给定一个字符串s和一个词典wordDict,确定s是否可以根据词典中的词分成一个或多个单词。
    比如,
    给定 s = "leetcode"
    wordDict = ["leet", "code"]
    返回true,因为"leetcode"可以被分成"leet code"
*/

public class LeetCode139_单词拆分 {
   
    /*
    状态:
        子状态:前1,2,3,...,n个字符能否根据词典中的词被成功分词
        F(i): 前i个字符能否根据词典中的词被成功分词
    状态递推:
        F(i): true{j <i && F(j) && substr[j+1,i]能在词典中找到} OR false
        在j小于i中,只要能找到一个F(j)为true,并且从j+1到i之间的字符能在词典中找到,
        则F(i)为true
    初始值:
        对于初始值无法确定的,可以引入一个不代表实际意义的空状态,作为状态的起始
        空状态的值需要保证状态递推可以正确且顺利的进行,到底取什么值可以通过简单
        的例子进行验证
        F(0) = true
    返回结果:F(n)
    */

    //注意边界值问题,因为在字符串中存储是从0下标开始,而在数组中实际是从1开始、arr[0]为辅助--用来判断
    public boolean wordBreak(String s, List<String> dict) {
   
        boolean[] canBreak = new boolean[s.length() + 1];
        // 初始化F(0)=true
        canBreak[0] = true;

        for (int i = 1; i <= s.length(); i++) {
   
            // true{j < i && F(j) && substr[j+1,i]能在词典中找到}
            for (int j = 0; j < i; j++) {
   
                // F(j): 之前的字符串已经确定了可以有从集合中找到分割串的结果为true
                // 如果从[j+1, i]这个子串也在集合中,那么相对的 F(i) 就找到了结果,为true
                if (canBreak[j] && dict.contains(s.substring(j, i))) {
    //substring左闭右开且字符向前挪一个
                    // 通过前面已知的 F(j) 从小到大来求 F(i)
                    canBreak[i] = true;
                    break;
                }
            }
        }
        return canBreak[s.length()];
    }

}

LeetCode120_三

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值