动态规划算法通常基于一个转移方程及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。使用动态规划来解题只需要多项式时间复杂度, 因此它比回溯法、暴力法等要快许多。
动态规划通常包含最优子结构——如果一个问题的最优解包含了子问题的最优解。
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