动态规划初阶

动态规划算法通常基于一个转移方程及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。使用动态规划来解题只需要多项式时间复杂度, 因此它比回溯法、暴力法等要快许多。

动态规划通常包含最优子结构——如果一个问题的最优解包含了子问题的最优解。

Question 1: 如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?

用d(i)=j来表示凑够i元最少需要j个硬币。我们可以推断:

  • d(0)=0
    • 不需要任何数量的硬币
  • d(1)=1
    • 这里只有1元硬币可以选择 d(1)=d(1-1)+1=d(0)+1=0+1=1
  • d(2)=2
    • 这里只有1元硬币可以选择 d(2)=d(2-1)+1=d(1)+1=1+1=2
  • d(3)=3
    • 这里我们可以选择3元硬币或者1元硬币
    • 如果选择3元硬币,问题就变成1+d(3-3)=1+d(0) = 1+0 = 1
    • 如果选择1元硬币,问题就变成1+d(3-1)=1+d(2) = 1+2 = 3
    • 那么这两种方案那种更好呢? 很明显第一种。min(1+d(0), 1+d(2)) = 1
  • d(i)表示凑够i元需要的最少硬币数量,我们将它定义为该问题的”状态”。
  • 最终我们要求解的问题,可以用这个状态来表示:d(11)
  • 之前我们提到d(3)=min{d(3-1)+1, d(3-3)+1},这就是状态的转移方程。
  • 那么抽象的转移方程可表示为:d(i)=min{d(i-Vj)+1},Vj表示第j个硬币的面值;
  • 每个状态转移方程都有出口点这里的出口点就是i-Vj<=0.就是一旦满足这个条件就return 0,结束递归。
#python
def getMinCoins(value,coins):
    if value<=0:
        return 0
    results=[]
    for coin in coins:
        if value-coin>=0:
            results.append(getMinCoins(value-coin,coins))
    return min(results)+1
coins = [1,3,5]
print(getMinCoins(3,coins))  #ouput = 1
print(getMinCoins(4,coins))  #ouput = 2
print(getMinCoins(11,coins)) #ouput = 3
Java版
public class Main {
    public static void main(String[] args) {
        int[] coins = {1,3,5};
        System.out.println(getMinCoins(3,coins));
        System.out.println(getMinCoins(4,coins));
        System.out.println(getMinCoins(11,coins));
    }
    public static int getMinCoins(int value, int[] coins){
        //程序的出口
        if(value<=0){
            return 0;
        }
        ArrayList results= new ArrayList<Integer>();
        for(int i = 0; i<coins.length;i++){
            if(value-coins[i]>=0)
                results.add(getMinCoins(value-coins[i],coins));
        }
        Collections.sort(results);
        return (int) results.get(0)+1;
    }
}

这里要注意:ArrayList排序的方法

Collections.sort(results);

Question 2 :一个序列有N个数:A[1],A[2],…,A[N],求出最长非降子序列(LIS)的长度。

假设序列为:

 5,3,4,8,6,7
  • 前1个数的LIS长度d(1)=1(序列:5)
  • 前2个数的LIS长度d(2)=1(序列:5,3——3前面没有比3小的)
  • 前3个数的LIS长度d(3)=2(序列:5,3,4——4前面有1个比它小的3,所以d(3)=d(2)+1)
  • 前4个数的LIS长度d(4)=3(序列:5,3,4,8——8前面比它小的有3个数,所以 d(4)=max{d(1),d(2),d(3)}+1=3)
  • 我们可以看出转移方程为:
d(i) = max{1, d(j)+1},其中j<i,A[j]<=A[i]
  • 方程退出递归条件为j=0.
#python
def getLIS(list,n):
    if n <=0:
        return 1
    result = [0]
    for i in range(0,n):
        if list[i] < list[n]:
            result.append(getLIS(list,i))
    return max(result)+1

list = [5,3,4,8,6,7]
print(getLIS(list,5))  #ouput is 4
    原文作者:crazydane
    原文地址: https://www.jianshu.com/p/94a434b9f1bf
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞