算法介绍
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。20世纪50年代初,美国数学家贝尔曼(R.Bellman)等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。动态规划的应用极其广泛,包括工程技术、经济、工业生产、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果
算法思想
动态规划的本质其实是一种分治思想,即把原问题分解为若干子问题,先求解最小的子问题,并将结果保存下来作为下一个子问题的参数,依次计算,最后得出原问题的解。
算法要素:
- 最优子结构:最优子结构性质是指问题的最优解包含其子问题的最优解。最优子结构是使用动态规划算法的最基本条件,如果不具备最优子结构的性质,就不可以使用动态规划算法解决。
- 子问题重叠:子问题重叠是指在求解子问题的过程中,有大量的子问题是重复的,那么只需要求解一次,然后把结果存储在最优子结构中,以便使用时可以直接查询,不需要再次求解。子问题重叠不是使用动态规划的必要条件,但问题存在子问题重叠能够充分彰显动态规划的优势。
算法实战--神奇的兔子
问题描述
假设第1个月有1对刚诞生的兔子,第2个月进入成熟期,第3个月开始生育兔子,而1对成熟的兔子每月会生1对兔子,兔子永不死去……那么,由1对初生兔子开始,12个月后会有多少对兔子呢?
问题分析
我们不妨拿新出生的1对小兔子分析:
第1个月,小兔子①没有繁殖能力,所以还是1对。
第2个月,小兔子①进入成熟期,仍然是1对。
第3个月,兔子①生了1对小兔子②,于是这个月共有2(1+1=2)对兔子。
第4个月,兔子①又生了1对小兔子③。因此共有3(1+2=3)对兔子。
第5个月,兔子①又生了1对小兔子④,而在第3个月出生的兔子②也生下了1对小兔子⑤。共有5(2+3=5)对兔子。
第6个月,兔子①②③各生下了1对小兔子。新生3对兔子加上原有的5对兔子这个月共有8(3+5=8)对兔子。
……
解题思路
一:首先构造最优子结构
兔子分为三个阶段:幼年期、成熟期、生育期。所以可以创建一个数据结构专门用于表示三个阶段的兔子的数量。
二:分析是否存在子问题重叠
本题中,每个月都会有生育期的兔子生小兔子、成熟期的兔子进入生育期、新生的兔子成为幼年期。所以,三种兔子的数量变化即这个问题的子问题。
解题代码
一:最优子结构
//用于保存兔子数量的数据结构
class RabbitsNum{
//幼年期
int childhood;
//成熟期
int mature;
//生育期
int growth;
RabbitsNum(){
this.childhood=0;
this.mature=0;
this.growth=0;
}
@Override
public String toString() {
return "childhood=" + childhood + " mature=" + mature + " growth=" + growth ;
}
}
二:重叠子问题解法
public class MagicRabbits {
private static RabbitsNum nextMonth(RabbitsNum rabbitsNum){
//成熟期的兔崽子可以生育了
rabbitsNum.growth += rabbitsNum.mature;
//幼年期兔崽子成熟了
rabbitsNum.mature = rabbitsNum.childhood;
//生育期的兔子下崽了
int newRabbit = rabbitsNum.growth;
//给刚出生的小兔崽子上户口
rabbitsNum.childhood = newRabbit;
return rabbitsNum;
}
public static void main(String[] args) {
RabbitsNum rabbitsNum = new RabbitsNum();
//初始兔子数量
rabbitsNum.childhood=1;
System.out.println("第1月 " + rabbitsNum);
//定义生产月份
int month = 12;
for (int i =2; i <= month; i++){
nextMonth(rabbitsNum);
System.out.println("第" + i + "月 " + rabbitsNum);
}
}
}
计算结果: