关于动态规划

60 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 26 天,点击查看活动详情

什么是动态规划

动态规划是一种分阶段求解决策问题的数学思想,它通过把原问题分解为简单的子问题来解决复杂问题。

我第一次接触动态规划是:斐波那契数列。

斐波那契数列:01124.....

第一个数是0,第二个是1,第三个等于第一个+第二个

f(n)=f(n-1)+f(n-2)

那么这个题代码实现最简单是递归:

int Fibonacci(int n){
if(n<=1) return n;
return Fibonacci(n-1)+Fibonacci(n-2)
}

画图如下

image.png

这个就是递归的过程。那么我们可以发现f(2)计算了两次,f(1)计算了3次,f(0)计算了2次。而他们其实按道理只需要记录一次。所以优化1,加入map存储计算过的数。(也可以使用数组,不过如果使用数组,在比较只时候需要重新遍历数组,当数据量很大时候,不方便)

定义一个map关键字是f几,值是结果。

Map<Integer,Integer> map_flag
int Fibonacci(int n){
if(n<=1) return n;
if(map_flag.containsKey(n)){
return map_flag.get(n);
}
return map_flag.put(n,Fibonacci(n-1)+Fibonacci(n-2))
}

那么如何使用动态规划方式完成?

拆解问题:如果n=1或者n=0 f(n)确定

其他f(n)=f(n-1)+f(n-2)是动态规划的状态转移方程;

那么求解f(n)变成了求解f(n-1)+f(n-2)。

image.png

接下来是跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

分解问题:

当n=1的时候,有一种跳法,当n=2有两种,当n=3,有 n=2解法+n=1解法=3 种。 当n=4,有:n=3解法+n=2解法

那么图示如下

image.png

动态规划代码如下:

image.png

关于最小跳台阶代价

给定一个整数数cost  ,其中cost[i]  是从楼梯第 i 个台阶向上爬需要支付的费用,下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

如果n<2 花费Math.min(cost[0],cost[1]);

如果n>2 花费当前cost+min(前面的)

dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

那么代码如下

image.png

看上去动态规划和递归一样,需要找到规律。

怎么输出最长子序列?

首先模板子序列不一定连续。那么如果当前str1的某个值和str2某值相等,那么当前值=前面值+1.

image.png

如图:假设当前A相等,那么最大序列是,A之前序列的最大序列+1.(dp[i][j]=dp[i-1][j-1]+1;)

如果当前不相等,那么其中一个字符串回退1,找到回退的最大值。(dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);)

边界条件:如果字符串为空,输出-1。然后在dp数组里面,找出这个字符子串。

image.png

你可能会疑惑,为什么会倒着加进去?因为我们是向前寻找,假设当前A相等,那么最大序列是,A之前序列的最大序列+1.

那么和子序列相关的是子串问题。

遍历数组,如果当前ij对于值相等,res++

image.png

总结:

动态规划其实就是怎么把大问题分成几个小问题,怎么通过小问题得到大问题,如果很难考虑的话,我们可以优先使用递归等方式实现,然后一步步优化。

动态规划需要找到:dp下标和含有,递推公式,怎么初始化,遍历顺序