牛客网常见算法思路 (十二)动态规划

有用麻烦点个赞哦

如何理解动态规划

经典题:给定一个数组arr,arr中所有的值都为正数且不重复。每个值代表着一种面值的货币,每种面值的货币可以使用任意张,在给定一个整数aim代表要找的钱数,求换钱有多少种哦方式

本题可以体现
暴力搜索方法->记忆搜索方法->动态规划方法->状态继续化简后的动态规划方法

暴力搜索

arr={5、10、25、1},aim=1000
1、用0张5元的货币,让[10,25,1]组成剩下的1000,最终方法数记为----res1
2、用1张5元的货币,让[10,25,1]组成剩下的995,最终方法数记为------res2
3、用2张5元的货币,让[10, 25,1]组成剩下的990,最终方法数记为-----res3
如此类推…
201、用200张5元的货币,让[10, 25,1]组成剩下的0,最终方法数记为-----res201

定义递归函数:int p1(arr,index,aim),他的定义是如果用arr[index…N-1]这些面值的钱组成aim==1000,返回总的方法数

评价:
写出来可能比较简单,但实际上有大量的重复部分,如0张10元,2张5元 与 1张10元,0张5元后续部分的匹配是一样的

记忆搜索算法 (其实已经可以认为是动态规划了)

因为这个过程中arr不变,变得是index和aim,以p(index,aim)进行递归,准备一个hashmap,
1、每计算完一个p(index,aim)都将结果放入map中,index和aim组成共同的key,返回结果为Value
2、要进入一个递归过程时,先以index和aim注册的key在map中查询,如果已存在则直接取值

动态规划方法

arr长度为N,则生成行数为N,列数为aim+1的矩阵dp。
dp[i][j]的含义是arr[0…i]货币的情况下,组成钱数j有多少种方法,最终目标为dp[N][aim+1],dp[i][j]=sum(dp[i-1][0到j])
改进:dp[i][j]=dp[i][该行上一个要统计的格子]+dp[i-1][j](上一行用一个格子)时间复杂度O(N*aim)

动态规划和记忆搜索方法的联系

1、记忆搜索为某种形态的动态规划方法
2、记忆搜索不关心到达某个过程的路径,只是单纯的计算递归过程,避免重复。3、动态规划的方法则是规定好每一个递归过程的计算顺序,依次进行计算,后面的计算过程严格依赖前面的计算过程。
4、两者都是空间换时间,也有枚举过程,区别在于有无规定的计算顺序

解题步骤:

1、实现暴力递归
2、暴力递归中查看哪些函数可以代表递归过程
3、找到代表递归过程的参数之后,记忆化搜索的方法非常容易实现
4、分析记忆搜索的依赖路径,找到动态规划
5、通过动态规划尝试化简

案例一

有n级台阶,每次走一级或者两级,问有多少种方法走完,
思路
1、可以用斐波拉切

案例二

给定矩阵m,从左下角开始每次只能向下或者向右走,路径上数字加起来的和为路径和,找出最小的路径和
思路
1、生成和m一样大小的矩阵dp
2、第一行的最小路径和第一列的最小路径可以直接累加走出(直走)
3、dp[i][j]=m[i][j]+min(dp[i-1][j],dp[i][j-1])

案例三

给定数组arr,返回arr的最长递增子序列长度。2,1,5,3,6,4,8,9最长递增子序列13489,返回5
思路
生成长度为n的数组db,db[i]为以arr[i]的个数结尾的情况下最长递增子序列的长度
过程:检索i时从左边找比i小的数,选bp最大的+1作为自己bp的值。
dp[i]=max{dp[j]+1(0<=j<i,arr[j]<arr[i])}

案例四

给定两个字符串str1和str2,返回两个字符串最长公共子序列(子序列不一定要连续)
思路
str1长度为M,str2长度为N,生成大小为M*N的矩阵dp。dp[i][j]的含义是str1[0…i]与str2[0…j]的最长公共子序列的长度
dp求法如下
1、dp第一列,dp[i][0],代表str1[0…i]与str2[0]的最长公共子序列长度。str2[0]只有一个字符,所以dp[i][0]最大为1。如果str1[i]==str2[0],则领dp[i][0]为1,一旦dp[i][0]为1,则令dp[i+1…M][0]全部为1,。
2、dp第一行与第一列同理,一旦dp[0][j]被设为1,则令dp[0][j+1…N]全部为1
3、其他位置dp[i][j]为一下三种情况
情况一:为dp[i-1][j]。代表着str1[0…i-1]与str2[0…j]的最长公共子序列长度
情况二:同理可知有可能是dp[i][j-1]
情况三:如果str[i]==str[j],还可能是dp[i-1][j-1]+1的值
选择三者中最大的值即可

案例五

背包问题
一个背包承重W,有N件物品,每件物品有他们的价值,记录在数组V也有他们的重量,记录在数组W,问如何选择背包总价值最大
思路
从1到n,一件一件考虑是否加入背包
假设dp[x][y]是前x件物品,不超过重量y的时候的最大价值:枚举第x件物品的情况:
情况一:如果选择第x件物品,则前x-1件物品不能超过y-w[x]
情况二:不选择x,前x件物品,x-1件物品不能超过y

所以dp[x][y]可能等于dp[x-1][y],也就是不取第x件物品时,价值和之前一样
也可能等于dp[x-1][y-w[x]]+v[x]
选择最大的
行数为物品数量n,列数为背包重量w,从左到右,从上到下,依次计算即可。

案例六

给定两个字符串str1和str2,在给定三个整数ic,dc和rc,分别代表插入删除和替换一个字符的代价。返回将str1编辑成str2的最小代价
思路
假设str1长度为m,str2长度n,生成大小为(M+1)*(N+1)的矩阵dp,dp[i][j]的值代表代表str1[0…i-1]编辑成str2[0…j-1]的最小代价

1、dp[0][0]代表str1空字符串编辑成str2空字符串的代价0.
2、矩阵dp第一列即dp[0…M][0],dp[i][0]表示str1/[0…i-1]编辑成空字符串的最小代价,即都删除dp[i][0]=dc*i

3、矩阵第一行同理,为str1空字符串编辑成str2的代价dp[0][j]=ic*j
4、其他位置先从左到右,从上到下,dp值只会来自四种情况
情况一:(从多出来的部分减一个)
str1[0…i-1]先编辑成str[0…i-2],也就是删除str1[i-1],再编辑成str2[0…j-1],dp[i-1][j]就表示str1[0…i-2]编辑成str2[0…j-1]的最小代价,所以dp[i][j]可能等于dc+dp[i-1][j]
情况二: (从少的部分加一个)
其实和一同理,dp[i][j-1]+ic
情况三:(替换一个字符)
dp[i-1][j-1]+rc
情况四:str1和str2新增的字符刚好相等。dp[i-1][j-1]
选择最小的值作为dp[i][j]的值

有用麻烦点个赞哦

发布了23 篇原创文章 · 获赞 24 · 访问量 3062

猜你喜欢

转载自blog.csdn.net/weixin_44303896/article/details/104177006