动态规划题目特点:
1、计数
有多少种方式走到右下角
有多少种方法选出k个数使得和是sum
2、求最大最小值
从左上角走到右下角路径的最大数字和
最长上升子序列长度
3、求存在性
取石子游戏,先手是否必胜
能不能选出k个数使得和是sum
问题一:你有三种硬币,分别面值2元、5元和7元,每种硬币都有足够多,买一部本书需要27元,如何用哪个最少的硬币组合正好付清,不需要对方找钱。
动态规划组成部分一:确定状态
状态在动态规划中的作用属于是定海神针
简单的说,解动态规划的时候需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么
类似于解数学题中,x,y,z代表什么
确定状态需要两个意识:
最后一步
子问题
最后一步:
虽然我们不知道最有策略是什么,但是最优策略肯定是k枚硬币a1,a2,…ak面值加起来是27
所以一定有一枚最后的硬币:ak
除掉这枚硬币,前面的硬币的面值加起来是27-ak
关键点1
我们不关心前面的k-1枚硬币是怎么拼出来27-ak的,而且我们现在甚至不知道ak和k,但是我们确定了前面的硬币拼出了27-ak
关键点2
因为是最优策略,所以频出27-ak的硬币数一定要最少,都则这不是最优策略了
子问题
所以我们要求:最少用多少枚硬币可以拼出27-ak
原问题是最少用多少枚硬币拼出27
我们将原问题转化成一个子问题,而且规模更小:27-ak
为了简化定义,我们设状态f(X)=最少用多少枚硬币拼出X
递归解法
int f(int x){
if (x==0) return 0;
int res = MAX_VALUE;
if(x>=2)
res = Math.min(f(x-2)+1,res);
if(x>=5)
res = Math.min(f(x-5)+1,res);
if(x>=7)
res = Math.min(f(x-7)+1,res);
return res;
}
但是递归效率低下,做了很多重复的工作
动态规划组成部分二:转移方程
动态规划组成部分三:初始条件和边界情况
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
初始条件f[0]=0;
动态规划组成部分四:计算顺序
拼出x所需要的最少硬币数:f[x] = min{f[x-2]+1,f[x+5]+1,f[x-7]+1}
初始条件:f[0]=0
然后计算f[1],f[2],…,f[27]
当我们计算到f[x]时,f[x-2],f[x-5],f[x-7]都已经得到结果了
求最值型动态规划
动态规划组成部分:
一、确定状态
最后一步(最优策略中使用的最后一枚硬币ak
化成子问题(最少的硬币拼出更小的面值27-ak)
二、转移方程
f[x] = min{f[x-2]+1,f[x-5]+1,f[x-7]+1}
三、初始条件和边界情况
f[0]=0,如果不能拼出Y,f[Y]=正无穷
四、计算顺序
f[0],f[1],f[2],…
消除冗余,加速计算
问题二:
给定一个m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下或者向右走一步
问有多少种不同的方式走到右下角
0,0 | 0,1 | 0,2 | 0,3 | 0,4 | 0,5 | 0,6 | 0,7 |
1,0 | 1,1 | 1,2 | 1,3 | 1,4 | 1,5 | 1,6 | 1,7 |
2,0 | 2,1 | 2,2 | 2,3 | 2,4 | 2,5 | 2,6 | 2,7 |
3,0 | 3,1 | 3,2 | 3,3 | 3,4 | 3,5 | 3,6 | 3,7 |
(计数型动态规划)
动态规划组成部分一:确定状态
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:
向右或者向下
右下角坐标为(m-1,n-1)
那么前一步机器人一定是在(m-2,n-1)或者(m-1,n-2)
子问题
那么,如果机器人有x种方式从左上角走到(m-2,n-1),有Y种方式从左上角走到(m-1,n-2),则机器人有(X+Y)种方式走到(m-1,n-1)
问题转化为,机器人有多少种方式从左上角走到(m-2,n-1)和(m-1,n-2)
原问题要求有多少种方式从左上角走到(m-1,n-1)
子问题
状态:设f[i][j]为机器人有多少种方式从左上角走到(i,j)
动态规划组成部分二:转移方程
对于任意一个格子(i,j)
f[i][j]=f[i-1][j]+f[i][j-1]
动态规划组成部分三:初始条件和边界情况
初始条件:f[0][0] = 1,因为机器人只有一种方式到左上角
边界情况:i=0或j=0,则前一步只能有一个方向过来->f[i][j]=1
动态规划组成部分:计算顺序
f[0][0] = 1
计算第0行:f[0][0],f[0][1],…,f[0][n-1]
计算第1行:f[1][0],f[1][1],…,f[1][n-1]
…
计算第m-1行:f[m-1][0],f[m-1][1],…,f[m-1][n-1]