动态规划系列题目学习

“如果你想的算法是正确的,那么它一定能够被证明,不然就是直觉”

什么是动态规划(DP)

谈起动态规划(DP),我们首先要了解什么是DP

动态规划的一个思想就是,用空间换时间,既然我在转移的过程中,把某个点(i,j)的dp(i,j)给求了出来,那么为了避免下次其他点转移到这个点又要重复工作,干脆就把已经求出来的dp(i,j)的值记录下来,那么下次再遇到的时候,直接查表。这样,可以保证每个点(i,j)都只求一次dp(i,j)。

因此动态规划又叫做填表法,就是说动态规划就是个填表游戏。
我认为动态规划 可以理解为记忆化搜索

可以参考
1、自底向上:思想是逆向的,但也能正向解答。两者是相同的,只是求解顺序不一样。
2、状态转移方程:对于这个,我只能说,暴力怎么解,动态规划就怎么解。因为求解动态规划的顺序是先暴力递归——带备忘录的递归(即先记录一些已经求出来的值,没必要每次都重新求)——动态规划。并且看博客多了的人会发现,其实递归的递归体就是动态规划的状态转移方程。不同的思考,得出的状态转移方程也不一样。
3、最优子问题:大问题分成小问题,小问题寻找最优解构成大问题的最优解。这一点不必太在意,因为求解的过程就是在求解小问题的最优解。

上面这些话,我已经听过加看过几十遍了,可是见到动态规划系列的题目还是要么没思路,要么直接暴力,重新学习记录一下
最后学习动态规划,光靠理论是不行的,得结合实战

动态规划题目的特点

1.计数:
有多少种方式走到右下角
有多少种方法选出k个数使得和是Sum

2.求最大最小值
从左上角走到右下角路径的最大数字和
最长上升序列长度

3.求存在性
取石子游戏,先手是否必胜,求存在性
能不能选出k个数使得和是Sum

例1

你有三种硬币,分别面值2元,5元,7元,每种硬币足够多 买一本书27元 ,如何用最少的硬币组合正好付清,不需要对方找钱

最少揭示了可以使用DP

DP组成部分 1

确定状态

状态是什么?
状态在动态规划中的作用属于定海神针

简单的说,解动态规划的是否需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么
类似于数学方程中的的x,y,z代表什么。

确定状态需要两个意识
:最后一步

在这里插入图片描述
ak最优,27-ak也要最优

:子问题转变成

在这里插入图片描述

· 等等,我们还不知道最后那枚硬币ak是多少
· 最后那枚硬币ak只可能是2,5或者7
· 如果ak是2,f(27)应该是f(27-2)+1(加上最后这一枚硬币2)
· 如果ak是5,f(27)应该是(27-5)+1(加上最后这一枚硬币5)
· 如果ak是7,f(27)应该是f(27-7)+1(加上最后这一枚硬币7)
· 除此以外,没有其他的可能了

在这里插入图片描述

硬币这道题递归也可以解

核心代码如下:


int f(int x)
{                                  //f(x)=最少用多少枚硬币拼出x 
	if(x==0)
	{
		return 0;                 //0元只需要0枚硬币 
	}
	int res = MAX_VALUE;           //初始化用无穷大 
	if(x>=2)                                      //最后一枚硬币是2元 
	{
		res=Math.min(f(x-1) + 1,res);
	}
	if(x>=5)                                                            //最后一枚硬币是5元 
	{
		res=Math.min(f(x-1) + 1,res);
	}
	if(x>=7)                                                                                 //最后一枚硬币是7元 
	{
		res= Math.min(f(x-7)+1,res);
	}
	return res;
 } 

但是递归过程中会计算大量重复计算过的值,它并不会记忆,因此很可能造成超时问题。

DP组成部分 2 转移方程

设状态f【x】=最少用多少枚硬币拼出x

对于任意的x,
在这里插入图片描述

DP组成部分 3 初始条件和边界情况

·f[X]=min{f[X-2]+1,f[X-5]+1,f[X-7]+1}
·两个问题:X-2,X-5或者X-7小于0怎么办?什么时候停下来?
·如果不能拼出Y,就定义f[Y]=正无穷
-例如f[-1]=f[-2]…=正无穷
·所以f[1]=min(f[-1]+1,f[-4]+1,f[-6]+1}=正无穷,表示拼不出来1。。。正无穷加1还是正无穷

初始条件:

f[0]=0;有些题目会定义多个f[0],f[1]… 初始条件就是用转移方程算不出来,但需要手动定义。很多题目都是初始条件f[0]=0,这道题也是。

边界情况:

不要数组越界(下或上)

DP组成部分 4 计算顺序

大多数一维从小到大,大多数二维就会从左到右,从上到下

初始条件:f[0]=0
然后计算f[1],f[2],…f[27]

确定顺序方法:当我们计算到f[x]时,f[x-2],f[x-5],f[x-7]都已经得到结果了,因此需要用循环来保证

每一步尝试三种硬币,一共27步

与递归算法相比,没有任何重复计算

算法时间复杂度(即需要进行的步数):27 * 3

原文地址:https://www.cnblogs.com/AmosAlbert/p/12832326.html