动态规划(二)-坐标型动态规划

一.坐标型动态规划

1.坐标型动态规划特点

  • 坐标型动态规划可以说是最简单的动态规划类型

  • 给定一个序列或网格

  • 需要找到序列中某个/些子序列或网格中的某条路径

    • 某种性质最大/最小
    • 计数
    • 存在性
  • 动态规划方程f[i]中的下标i表示以ai为结尾的满足条件的子序列的性质,f[i][j]中的下标i,j表示以格子(i,j)为结尾的满足条件的路径的执行

    • 最大值/最小值
    • 个数
    • 是否存在

2.例1

  • 给定m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下或者向右走一步
  • 网格中有些地方有障碍,机器人不能通过障碍格
  • 问有多少种不同的方式走到右下角

在这里插入图片描述

动态规划组成部分一:确定状态
  • 这题和上章某题很像,只是网格中可能有障碍
  • 最后一步一定是从左边(i,j-1)或上边(i-1,j)过来,这点是没有变的,变的只是多了障碍而已
  • 状态f[i][j]表示从左上角有多少种方式走到格子(i,j)(坐标型动态规划基本都是:数组下标[i][j]即坐标(i,j))
动态规划组成部分二:转移方程
  • f[i][j]=f[i-1][j]+f[i][j-1]
动态规划组成部分三:初始条件和边界情况
  • 初始条件和边界情况
    • 如果左上角(0,0)或者右下角(m-1,n-1)格有障碍,直接输出0
    • 如果(i,j)有障碍, f[i][j]=0,表示机器人不能到达此格子(0种方式)
    • 初始条件: f[0][0]=1
      在这里插入图片描述
动态规划组成部分四:计算顺序
  • 计算第0行,计算第1行…
  • 计算第m-1行
  • 答案是f[m-1][n-1]
  • 时间复杂度(计算步数):O(MN),空间复杂度(数组大小):O(MN)
代码
class Solution {
   
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
   
        if(obstacleGrid==null||obstacleGrid.length==0){
   
            return 0;
        }
        int [] [] dp = new int[obstacleGrid.length][obstacleGrid[0].length];
        for(int i=0;i<obstacleGrid.length;i++){
   
            for(int j=0;j<obstacleGrid[i].length;j++){
   
                if(obstacleGrid[i][j]==1){
   //有障碍
                    dp[i][j]=0;
                    continue;
                }else{
   
                    if(i==0&&j==0) dp[i][j]=1;//没有障碍起点
                    else if(i==0) dp[i][j]=dp[i][j-1];//在第一行,上一题在没有障碍情况下默认是1,但是有障碍就取决于该行前面是否有障碍了
                    else if(j==0) dp[i][j]=dp[i-1][j];//第一列,没有障碍默认是1,有障碍就取决于该列前面是否有障碍了
                    else{
   
                         dp[i][j]=dp[i-1][j]+dp[i][j-1];
                    }
                }
                
            }
        }
        return dp[obstacleGrid.length-1][obstacleGrid[0].length-1];
    }
}

3.例2(最长连续单调子序列)[Longest Increasing Continuous Subsequence]

  • 题意
    给定a[0],…a[n-1],找到最长的连续子序列i,i+1,i+2,…j,使得a[i]<a[i+1]<…<a[j],或者a[i]>a[i+1]]>…>a[j],输出长度j-i+1
  • 例子
    • 输入:[5,1,2,3,4]
    • 输出:4(子序列1,2,3,4)
A.思考
  • 首先,对于a[i]>a[i+1]>…>a[j],可以将整个a蓄力倒过来,就变成球最长连续上升子序列了
  • 所以,只需要考虑找到最长的a[i]<a[i+1]<…<a[j]
  • 可以从每个a[i]开始,一直向后延伸找到最长的连续上升序列
  • 最差情况下,对于长度为N序列,需要计算O(n²)步(0,1,2,3…,n-1)
B.确定状态
  • 最后一步:对于最优的策略,一定有最后一个元素a[j]
  • 第一种情况:最优策略中最长连续上升子序列就是{a[j]},答案是1
  • 第二种情况,子序列长度大于1,那么最优策略中a[j]前一个元素肯定是a[j-1],这种情况一定是a[j-1]<a[j],因为是最优策略,那么它选中的以a[j-1]结尾的连续上升子序列一定是最长的
  • 子问题
    • 要求以a[j-1]结尾的最长连续上升子序列,本来是求以a[j]结尾的最长连续上升子序列
    • 化为子问题
  • 状态:f[j]=以a[j]结尾的最长连续上升子序列的长度
C.转移方程
  • f[j]=以a[j]结尾的最长连续上升子序列的长度
    在这里插入图片描述
D.初始条件和边界情况

在这里插入图片描述

E.计算顺序
  • f[j]=以a[j]结尾的最长连续上升子序列的长度
  • 计算f[0],f[1],…f[n-1]
  • 和硬币组合题不一样的是,最终答案并不是f[n-1]
  • 因为不知道最优策略中最后一个元素是那个a[j]
  • 所以答案是max{f[0]、f[1]、…f[n-1]}
  • 算法时间复杂度和空间复杂度都是O(n)【如何做到空间复杂度O(1)?-覆盖,滚动数组】
F.代码
	   public void calc(int [] A,int n){
   
           int [] f= new int[n];
           for(int i=0;i<n;i++){
   
               f[i]=1;
               if(i>0&&A[i-1]<A[i]){
   
                   f[i]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值