动态规划初级篇(上)_动态规划序列_南 栀的博客-程序员宝宝

技术标签: 算法  java  动态规划  

大家好,我是耀星,欢迎来到动态规划频道,如何你是一枚新手的话,建议大家能够先学习动态规划入门篇

目录

        动态规划题目特点

        Example1 (换硬币)

        Example2 (跳跃游戏)

        Example 3 (解密方法)

        Example 4(最长升序子串)

        Example 5(调手表)

        坐标型动态规划总结

动态规划题目特点

1.计数

        -有多少种方式走到终点

        -有多少种方式选出k个数的和相同

         ...

2.求最大值最小值

        -求路径的最大最小数字和

        -最长上升子序列长度

        ...

3.求存在性

        -取石子游戏,先手是否必胜 

        -能不能选出k个数和是sum

        ... 

Example1 (换硬币)

 你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多,假设买一本书需要27元,如何用最少的数量的硬币买到这本书,如果给定金额不能够拼出返回-1。

LintCode链接:换硬币

1.确定状态

最后一步:

最后一步我们可能选2元,选择5元,选择7元,如果告诉你拼出25元最少要5枚,拼出22元最少要5枚,拼出20元最少要四枚。那么你最后当然会选择7元作为最后一枚硬币,拼出27元。

子问题:

如果最后一步是选择2元

dp[27] = dp[27-2]+1;//转化为求25元能够用最少硬币拼出

如果最后一步是选择5元

dp[27] = dp[27-5]+1;//转化为求22元能够用最少硬币拼出

如果最后一步是选择7元

dp[27] = dp[20] +1;//转化为求20元能够用最少硬币拼出

我门只是做了假设最后一步应该怎么走,那么具体怎么走才是最优的呢?

dp[27] = min{dp[20],dp[25],dp[22]}+1

 2.转移方程

 3.初始条件和边界情况

dp[0] = 0;//0元用0枚硬币拼出

如过i-k<0时,则不能够拼出我们想要的数,设不能拼出为+∞。

例如我们想拼出dp[3]

4.计算顺序

dp[0] dp[1] dp[2] dp[3]....dp[n]

说明:题目中并没有给定确定的硬币面值,但是我们同样可以通过推导得出dp[i]=min{dp[i-k1],dp[i-k2],dp[i-k3]...dp[i-kn]}+1 .

public class Solution {
    public int coinChange(int[] coins, int amount) {
    int m = coins.length;
    if(m == 0 || amount == 0){
        return 0;
    }
    
    int[] dp = new int[amount+1];
    
    //初始化
    dp[0] = 0;
    
    for(int i=1; i<=amount; i++){
        dp[i] = Integer.MAX_VALUE;
        
        //有m个不同的硬币
        for(int j=0; j<m; j++){
            if(i >= coins[j] && dp[i-coins[j]] != Integer.MAX_VALUE){//处理边界情况
                dp[i] = Math.min(dp[i-coins[j]]+1,dp[i]);//转移方程
            }
          }
    }
        //拼不出返回-1
        if(dp[amount] == Integer.MAX_VALUE){
            return -1;
        }
        return dp[amount];
    }
}

小结

最值型动态规划

动态规划组成部分

       -1.确定状态

              最后一步(最后一步应该如何选择硬币k)

              子问题(选择最少的硬币,子问题dp[i-coins[j]]

       2.转移方程

              dp[i] = Math.min{dp[i-2],dp[i-5],dp[i-7]}+1;

        3.初始条件和边界情况

             dp[0] = 0;

             不能拼出i,dp[i] = Integer.MAX_VALUE;

Example2 (跳跃游戏)

给出一个非负整数数组,你最初定位在数组的第一个位置。数组中的每个元素代表你在那个位置可以跳跃的最大长度。判断你是否能到达数组的最后一个位置。

样例

输入:A=[2,3,1,1,4]

输出:true

LintCode链接:跳跃游戏

1.确定状态

设定一个数组dp[i]表示是否能够跳到第i个位置,能够跳到为true,不能够跳到为false.

最后一步:从数组的第一个位置开始跳,最后一步要求跳到第n-1块石头,设最后一步是从第i块石头跳到n-1

子问题:是否能够跳跃到第i个位置

原问题:是否能够跳到第n-1个位置

想要跳到n-1,第i个位置需要满足的条件有:

dp[i] == true

i+A[i] >= n-1//从第i个位置开始跳,i+A[i]能够跳到的最大位置>=n-1,那么就能跳到第n-1个位置

状态:dp[i]表示能否跳到第i个位置

2.转移方程

用变量j向前枚举

3.初始条件和边界情况

起始位置为数组的第一个元素,所以第一个位置一定能够跳到

dp[0] = true 

4.计算顺序 

dp[0] dp[1] dp[2] dp[3]...dp[n-1]

public class Solution {
    public boolean canJump(int[] A) {

        int n = A.length;
        if(n == 0 || A==null){
            return false;
        }

        boolean[] dp = new boolean[n];
        dp[0] = true;

        for(int i=1; i<n;i++){
            dp[i] = false;
            for(int j = i-1;j>=0;j--){
                if(dp[j] == true && j+A[j] >= i){
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[n-1];
    }
}

Example 3 (解密方法)

解密方法:有一段数字序列,将该段数字序列解密成字符串,一共有多少种解密方法。

1-A  2-B 3-C... 26-Z

例如:

12:L或AB

LintCode链接:解密方法

1.确定状态

最后一步:最后一个字符可能由两位数组成,也有可能由一位数组成。

例如:128439516 

将数字序列划分成若干段数字序列

6为最后一个数字,最后一步可以拼成一个F,16作为最后一个数字也可以可以解密为一个P,如果告诉你N-1个数可以拼出的N1种方案,N-2个数可以拼出N2种方案,那么你一定会计算N个数有多少种解密方法。

子问题:求N-1和N-2个数字序列的解密方法

原问题:求N个数字序列的解密方法

状态:前i个数字序列,有dp[i]种解密方法

2.转移方程

3.初始条件和边界情况 

初始条件:dp[0] = 1即空串有一种解密方式

边界情况:i == 1,只看最后一个数字

当最后一位是0时,可能由两位数字组成,若两位都为0或两位大于26则不能组成字符串,所以先默认dp[i] = 0,若不满足组成字符条件,则dp[i]就为0。

4.计算顺序

 dp[0] dp[1] dp[2] dp[3] ...dp[n]

public class Solution {
=
    public int numDecodings(String s) {

        char[] ss = s.toCharArray();
        int n = ss.length;

        if(n == 0){
            return 0;
        }

        int[] dp = new int[n+1];
        dp[0] = 1;

        for(int i=1; i<=n; i++){
            
           dp[i] = 0;先初始化为0,可能存在不能组成字符串的情况
            
           //用一个字符作为结尾
           int t1 = ss[i-1]-'0';
           if(t1>0 && t1<=9){//若t == 0,自动选择后两位作为一个字符
               dp[i]+=dp[i-1];
           }
          
           if(i>=2){
               //取出倒数第二位
              int t2 = (ss[i-2]-'0')*10+ss[i-1]-'0';

               if(t2<=26 && t2>=10){//最后两位小于26并且大于等于10才能组成字符
                   dp[i]+=dp[i-2];
               }       
           }

        }

        return dp[n];
    }
}

Example 4(最长升序子串)

给定一个整数数组(下标从 0 到 n-1, n 表示整个数组的规模),请找出该数组中的最长上升连续子序列。(最长上升连续子序列可以定义为从右到左或从左到右的序列。)

LintCode链接:最长升序子串

输入:[5, 4, 2, 1, 3]
输出:4
解释:
给定 [5, 4, 2, 1, 3],其最长上升连续子序列(LICS)为 [5, 4, 2, 1],返回 4。

1.确定状态

通过最优策略选择出最长升序子串的长度

最后一步 :如果告诉你以A[i-1]为结尾的升序子串长度为为N1,如果A[i]>A[i-1],那么以A[i]为结尾的升序子串长度为N1+1,如果A[i]<A[i-1],那么以A[i]为结尾的升序子串长度为1。

子问题:只要知道A[i-1]作为升序子串中最后一个元素的最长升序子串的长度。

原问题:求A[i]前的最长升序子串的长度。

状态:使用dp[i]记录以A[i]元素为为结尾的升序子串中最后一个元素的长度。

例如:

2.转移方程 

3.初始条件和边界情况

初始条件:

dp[0] = 1

4.计算顺序

dp[0] dp[1] dp[2] dp[3]...dp[n-1] 

public class Solution {
    //计算最长升序子列
    public int MyLongstIncreasingContinuousSubsequence(int[] A){
        int n = A.length;

        if(n == 0 || A == null){
            return 0;
        }

        int[] dp = new int[n];
        //初始化
        dp[0] = 1;
        int max = 1;//记录最长子串

        for(int i=1; i<n;i++){
            dp[i] = 1;//若没有递增默认为1

            if(A[i]>A[i-1]){
                dp[i] = Math.max(1, dp[i-1]+1);
                max = Math.max(dp[i],max);
            }
        }
        return max;
    }
    public int longestIncreasingContinuousSubsequence(int[] A) {

        int ret1 = MyLongstIncreasingContinuousSubsequence(A);
        
        //倒置字符串,计算最长降序子串
        int left = 0;
        int right = A.length-1;
        while(left < right){
            int temp = A[left];
            A[left] = A[right];
            A[right] = temp;
            left++;
            right--;
        }

        int ret2 = MyLongstIncreasingContinuousSubsequence(A);

        int result = Math.max(ret1, ret2);

        return result;
    }
}

Example 5(调手表)

小明买了块高端大气上档次的电子手表,他正准备调时间呢。在 M78 星云,时间的计量单位和地球上不同,M78 星云的一个小时有 n分钟。大家都知道,手表只有一个按钮可以把当前的数加一。在调分钟的时候,如果当前显示的数是 0 ,那么按一下按钮就会变成 1,再按一次变成 2 。如果当前的数是 n - 1按一次后会变成 0。作为强迫症患者,小明一定要把手表的时间调对。如果手表上的时间比当前时间多 1,则要按 n - 1 次加一按钮才能调回正确时间。小明想,如果手表可以再添加一个按钮,表示把当前的数加 k 该多好啊......他想知道,如果有了这个+k 按钮,按照最优策略按键,从任意一个分钟数调到另外任意一个分钟数最多要按多少次。

注意,按 +k按钮时,如果加 k 后数字超过 n-1则会对 n 取模。

比如,n=10, k=6 的时候,假设当前时间是 0,连按 2 次 +k+k 按钮,则调为 2。

输入描述

一行两个整数 n, k(0 < k < n <10^5) (0<k<n≤10^5)意义如题。

输出描述

输出一行一个整数,表示按照最优策略按键,从一个时间调到另一个时间最多要按多少次。

OJ链接:调手表

1.确定状态

最后一步:

最后一步要调到n-1的位置,可以从n-1-1 || n-1-k || (j*k)%n的位置调到n-1。

子问题:

如果最优策略是从i-1~i

如果最优策略是从i-k~i

如果最优策略是(j*k)%n~i

转化为求调到时间i-1或i-k或(j*k)%n的最优策略

原问题:求调第到第n-1分钟的最优策略

状态:dp[i]为调第i分钟,按表的最少次数。

2.转移方程

3.初始条件及边界情况

初始条件:

dp[0] = 0

4.计算顺序

dp[0] dp[1] dp[2] dp[3]...dp[n-1] 

import java.util.Scanner;
import java.util.Arrays;

public class Solution {
    public int MinNumberOfTime {
        Scanner scan = new Scanner(System.in);
        
        int n = scan.nextInt();
        int k = scan.nextInt();

        int[] dp = new int[n];
        dp[0] = 0;初始化

        for(int i=1;i<n;i++){
          dp[i] = dp[i-1]+1;
      
          if(i-k>=0 && k>1){
            dp[i] = Math.min(dp[i-k]+1,dp[i]);
          }

          for(int j=2;j<dp[i];j++){//寻找可行的j
            
            if((j*k)%n == i){
              dp[i] = Math.min(dp[i],j);
              break;
            }

          }
        }

        排序找到最大数
        Arrays.sort(dp);

        return dp[n-1]
    }
}

坐标型动态规划总结

给定输入为序列或者网格

动态规划状态下标为序列下标i或者坐标(i,j)

--dp[i]表示以i个元素结尾的某种性质

--d[i][j]表示以坐标(i,j)为结尾的性质

初始化设置dp[0]或dp[0][0...n]的值

二维空间优化:滚动数组节省空间

水平有限,希望能够对大家有帮助。感谢大家的支持! 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_52763385/article/details/123630890

智能推荐

黑马程序员------------JAVA笔记问答03_wsl_wangshenglong的博客-程序员宝宝

什么是正则表达式?答案:正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则达表示通常被用来检索、替换那些符合某个模式的文本。使用BigInteger类与BigDecimal类可以解决哪些问题?答案BigInteger支持任意精度的整数。 BigDecimal支持任意精度的定点数。 通过二者可以实现高精度的科学运算,避免数据不准确的问题。

Android设计模式------数据结构模式_浪里小黑狼的博客-程序员宝宝

"数据结构"模式使用场景常常有一些组件在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大地破坏组件的复用。这时候,将这些特定数据结构封装在内部,在外部提供统一的接口,来实现与特定数据结构无关的访问,是一种行之有效的解决方案。典型模式●Composite(组合模式)●Iterator(迭代器)●Chain of Resposibility(责任链)...

delphi中更新表字段update BLOB型数据(image)/获取图片转成BLOB型存入数据库_delphi updata_阿_洁的博客-程序员宝宝

我用的是ODAC 链接oracle数据库,delphi中update BLOB型数据问题困扰了我很久,我发现我直接v_sql :='update xx_image set image:=in_image where xx1='+xx1;                  with dm_db.OraQuery1 do                  begin

pwm控制舵机转动角度程序_STM32Cube21(补充) | 使用通用定时器产生PWM驱动舵机..._weixin_39845113的博客-程序员宝宝

点上方蓝字关注我们每天都有好玩的东西等着你本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的通用定时器外设,产生PWM驱动舵机。1. 准备工作硬件准备开发板首先需要准备一个开发板,这里我准备的是STM32L4的开发板(BearPi):小熊派IoT开发套件舵机这里我使用常见的 SG90 舵机:9g舵机知识小卡片 —— 舵机舵机是电机的一种,又叫伺服电机,舵机的优...

sql server 常见错误代码15000 - 15999含义解析_请叫我头头哥的博客-程序员宝宝

错误 15000 - 15999SQL Server 2008 R2其他版本 错误严重性是否记录事件说明(消息正文)1500116否对象 '%ls' 不存在或不是此操作的有效对象。1500216否

MHA报错:Bareword “FIXME_xxx“ not allowed while “strict subs“_闪电和风暴的博客-程序员宝宝

Bareword "FIXME_xxx" not allowed while "strict subs" in use at /usr/local/bin/master_ip_failover line 103.

随便推点

阿里云服务器配置选择方法和经验(CPU+内存+宽带)_weixin_33681778的博客-程序员宝宝

阿里云ECS云服务器配置的选择不仅仅包括CPU核数、内存及宽带多少,还需要根据实际业务场景选择对应的规格族,云吞铺子分享阿里云服务器的选配方法和经验:云服务器的CPU+内存选配普通的个人小型网站,如:个人博客等小流量网站,可选择入门级配置的云服务器推荐配置:1核CPU、1G或2G内存、硬盘40G、1M或2M带宽论坛、门户类网站:论坛、门户类网站,...

敏捷开发之道(一)敏捷开发宣言_个体和交互的理解正确的是_二三四的博客-程序员宝宝

从本篇文章开始,我们一起来探讨一下敏捷开发的相关内容。

Redis的hyperloglog_redistemplate.opsforhyperloglog()_散_步的博客-程序员宝宝

互联网名词:什么是UV?Unique Visitor,独立访客,一般理解为客户端IP,需要去重考虑什么是PV?Page View,页面浏览量,不用去重什么是DAU?Daily Active User 日活跃用户量登录或者使用了某个产品的用户数(去重复登录的用户)常用于反映网站、互联网应用或者网络游戏的运营情况什么是MAU?MonthIy Active User 月活跃用户量看需求:统计某个网站的UV、统计某个文章的UV用户搜索网站关键词的数量统计用户每天搜索不同词条

帝国cms常用标签 整理的应该是很全面_"[e:loop={\"select * from phome_enewsclass where…"_kassilu的博客-程序员宝宝

帝国cms常用标签 整理的应该是很全面大家会经常遇到帝国cms常用标签忘记的情况,我也是好久没写了,今天写的时候有几个忘记了,所以吧常用的几个整理出来,备注在我的博客,等用的时候可以第一时间找到,当然也方便大家使用,有过有缺失的,可以联系我加上1、loop获取时间标签/*获取年月日,时分秒。可以按照自己的需求单独获取年,或者月。*/&lt;?=date("Y-m-d H:i:s",$bqr[newstime])?&gt;2、更新url地址,啥的,需要清除原先的url缓存,然后清楚缓存,重新更.

【有利可图网】PS教程:设计制作一个逼真的泥土字体效果_写大一点的泥字用啥工具_ketuweb的博客-程序员宝宝

本篇教你如何利用PS设计制作一个逼真的泥土字体效果!教程讲解详细易懂,最终完成效果很炫酷,感兴趣的小伙伴赶快收走学起来吧!设计师:罗磊泥土字体素材:链接:https://pan.baidu.com/s/1dCTJkg3enoXMX1eIbMa-FA提取码:0829...

推荐文章

热门文章

相关标签