动态规划,它来了(一)

joytrian
发布于 2022-7-7 17:02
浏览
0收藏

 

前言
大家好,我是bigsai,好久不见,甚是想念(天天想念)!

很久前就有小伙伴被动态规划所折磨,确实,很多题动态规划确实太难看出了了,甚至有的题看了题解理解起来都费劲半天。

动态规划的范围虽然确实是很广很难,但是从整个动态规划出现的频率来看,这几种基础的动态规划理解容易,学习起来压力不大,并且出现频率非常高。

动态规划,它来了(一)-鸿蒙开发者社区

这几个常见的动态规划有:连续子数组最大和,子数组的最大乘积,最长递增子序列(LIS),最长公共子序列(LCS),最长公共子串,最长公共子串,不同子序列。

什么是动态规划
首先很多人问,何为动态规划?动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。通俗一点动态规划就是从下往上(从前向后)阶梯型求解数值。

那么动态规划和递归有什么区别和联系?

总的来说动态规划从前向后,递归从后向前,两者策略不同,并且一般动态规划效率高于递归。

不过都要考虑初始状态,上下层数据之间的联系。很多时候用动态规划能解决的问题,用递归也能解决不过很多时候效率不高可能会用到记忆化搜索。

不太明白?

就拿求解斐波那契额数列来说,如果直接用递归不优化,那么复杂度太多会进行很多重复的计算。

动态规划,它来了(一)-鸿蒙开发者社区

但是利用记忆化你可以理解为一层缓存,将求过的值存下来下次再遇到就直接使用就可以了。

动态规划,它来了(一)-鸿蒙开发者社区

实现记忆化搜索求斐波那契代码为:

static long F(int n,long record[])
{
  if(n==1||n==2) {return 1;}
  if(record[n]>0)
    return record[n];
  else
    record[n]=F(n-1,record)+F(n-2,record);
  return record[n];
}
public static void main(String[] args) {
  int n=6;
  long[] record = new long[n+1];
  System.out.println(F(n,record));
}

而动态规划的方式你可以从前往后逻辑处理,从第三个开始每个dp都是前两个dp之和。

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

当然动态规划也能有很多空间优化,有些只用一次的值,你可以用一些变量去替代。有些二维数组很大也可以用一维数组交替替代。当然动态规划专题很大,有很多比如树形dp、状压dp、背包问题等等经常出现在竞赛中,能力有限这里就将一些出现笔试高频的动态规划!

连续子数组最大和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。


dp的方法就是O(n)的方法。如果dp[i]表示以第i个结尾的最大序列和,而这个dp的状态方程为:

dp[0]=a[0]
dp[i]=max(dp[i-1]+a[i],a[i])

也不难解释,如果以前一个为截至的最大子序列和大于0,那么就连接本个元素,否则本个元素就自立门户。

动态规划,它来了(一)-鸿蒙开发者社区

实现代码为:

public int maxSubArray(int[] nums) {
    int dp[]=new int[nums.length];
    int max=nums[0];
    dp[0]=nums[0];
    for(int i=1;i<nums.length;i++)
    {
      dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
      if(dp[i]>max)
        max=dp[i];
    }
    return max;
}

ps:有小伙伴问那求可以不连续的数组最大和呢?你好好想想枚举一下正的收入囊中,那个问题没意义的。

连续子数组最大乘积
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

示例 :

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。


连续子数组的最大乘积,这也是一道经典的动态规划问题,但是和普通动态规划又有点小不同。

如果数据中都是非负数,对于连续数组的最大乘积,那样处理起来和前面连续子数组最大和处理起来有些相似,要么和前面的叠乘,要么自立门户。

dp[0]=nums[0]
dp[i]=max(dp[i-1]*a[i],a[i])

但是这里面的数据会出现负数,乘以一个负数它可能从最大变成最小,并且还有负负得正就又可能变成最大了。

这时候该怎么考虑呢?

容易,我们开两个dp,一个dpmax[]记录乘积的最大值,一个dpmin[]记录乘积的最小值。然后每次都更新dpmax和dpmin不管当前值是正数还是负数.这样通过这两个数组就可以记录乘积的绝对值最大。

动态方程也很容易

dpmax[i]=max(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i],nums[i])
dpmin[i]=min(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i],nums[i])

看一个过程就能理解明白,dpmin就是起到中间过度的作用,记录一些可能的负极值以防备用。结果还是dpmax中的值。

动态规划,它来了(一)-鸿蒙开发者社区

文章转自公众号:bigsai

标签
已于2022-7-7 17:02:57修改
收藏
回复
举报
回复
    相关推荐