动态规划自学笔记(1)

声明:本文记录自某动态规划公开课内存,并非原创!

动态规划题目特点

1.计数:

  • 有多少种方式走到右下角
  • 有多少种方式选出k个数使得和是Sum

2.求最大最小值

  • 从左上角走到右小角路径的最大数字和
  • 最长上升子序列长度

3.求存在性

  • 取石子游戏,先手是否必胜
  • 能不能选出K个数使得和是Sum

例:

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

思路:

1.动态规划组成部分一:确定状态

  • 状态在动态规划中的作用属于定海神针!!
  • 简单的说,解动态规划的时候需要开一个数组,数组的每一个元素f[i]或者f[i][j]代表什么:
  • 确定状态需要两个意识:

     1)最后一步:

虽然不知道最优策略是什么,但是最优策略肯定是K枚硬币a1,a2,…,ak面值加起来是27;

最后一步对应的就是最优策略中的最后一个决策。所以一定有一枚最后的硬币:ak;

除掉这枚硬币,前面硬币的面值加起来是27-ak;

关键点1:我们不关心前面的K-1枚硬币是怎么拼出27-ak的(可能有1种拼法可能有100种拼法),而且我们现在甚至还不知道ak和K,但是我们确定前面的硬币拼出了27-ak.

关键点2:因为是最优策略,所以拼出27-ak的硬币数一定要最少,否则这就不是最优策略了

     2)子问题

所以我们就要求:最少用多少枚硬币可以拼出27-ak

我们将原问题转化成了一个子问题,而且规模更小:27-ak

为了简化定义,我们设状态f(x)=最少用多少硬币拼出X

问题是,到现在还不知道最后那枚硬币ak是多少!!但最后那枚硬币ak只可能是2,5或者7.

如果ak是2,f(27)=f(27-2)+1;如果ak是5,f(27)=f(27-5)+1;如果ak是7,f(27)=f(27-7)+1;

需要求最少的硬币数,所以:

f(27)=min{ f(27-2)+1 , f(27-5)+1 , f(27-7)+1 }

递归解法伪代码:

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

}

递归解法的问题;

《动态规划自学笔记(1)》

效率低下:上图为递归程序的执行树,可以看出f(20)执行了3次,f(15)执行了2次….(不光是这几个点本身算了很多次,他们下面的全部计算分支也都算了很多次)

如何避免?

动态规划思想的解法是将计算结果保存下来,并改变计算顺序

2.动态规划组成部分二:转移方程

  • 设状态f[X]=最少用多少枚硬币拼出X
  • 对于任意X,f[X]= min{ f[x-2]+1 , f[x-5]+1 , f[x-7]+1 }

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
  • 初始条件:f[0]=0

初始条件其实就是用转移方程算不出来的,但我们却需要它,这是就手工定义它们!

边界情况在这个案例中就是不要数组越界:不管是往下越界还是往上超过了都不行!

4.动态规划组成部分四:计算顺序

  • 拼出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]时,f[x-2],f[x-5],f[x-7]

《动态规划自学笔记(1)》

  • 每一步尝试三种硬币,一共27步
  • 与递归算法相比,没有任何重复计算
  • 算法时间复杂度(即需要进行的步数):27*3,凑n块钱共m种硬币则是n*m次计算

5.小结

·求最值型动态规划

·动态规划组成部分;

1)确定状态

最后一步(最优策略中使用的最后一枚硬币ak)

化成子问题(最少的硬币拼出更小的面值27-ak)

2)转移方程

把想法换成式子

3)初始条件和边界情况

·消除冗余,加速计算

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

点赞