动态规划
什么是动态规划
简而言之,就是计算并存储小问题的解,并将这些解组成大问题的解。
动态规划解决步骤
1.确定状态:明确状态含义
2.确定转移公式:将状态间关系确定
3.确定边界条件:明确边界信息
4.计算结果
案例1:换零钱(凑出指定金额所需最少的硬币数)
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
解决
1.确定状态:设dp[i]表示金额为i时所需的最少硬币数。
2.确定转移公式:dp[i]表示金额为i时所需最少金额,假设选择金额为35,有1块,2块,5块以及10块4种硬币。那么现在选择1块钱问题就变为1+dp[34],选择2块钱问题就变为1+dp[33],选择5块钱问题就变为1+dp[30],选择10块钱问题就变为1+dp[25],其中1+代表现在已经使用了一个硬币,所以结果加1。而dp[35]就等于这4种情况的最小值。即dp[35]=min(1+dp[34],1+dp[33],1+dp[30],1+dp[25])。同样dp[34]也可安装刚才思路推理下去dp[34]=min(1+dp[33],1+dp[32],1+dp[29],1+dp[24])。由此可得出状态转移公式为 dp[n]=min(1+dp[n-coins[j]])
动态规划就是这样将大问题化解为小问题,并将小问题的解先存储起来,再计算大问题时用小问题的解一步一步化解出来。
3.确定边界条件:dp[0]即金额为0时所需硬币数,故dp[0]=0。当然还有可能永远也凑不出指定金额的情况,比如总金额为7,硬币种类为[2,10]。这样永远也凑不出7来。故需要设定一个值来判定是否凑不出
4.计算结果:
public int coinChange(int[] coins, int amount) {
int []dp=new int[amount+1];
int min=0;
for (int i = 1; i <amount+1 ; i++) {
dp[i]=amount+1;
}
for (int i = 0; i < dp.length; i++) {
min=amount+1;
dp[0]=0;
for (int j = 0; j < coins.length; j++) {
if(i>=coins[j]) {
dp[i] = 1 + dp[i - coins[j]];
dp[i] = Math.min(min, dp[i]);
min=dp[i];
}
}
}
return dp[amount]>amount?-1:dp[amount];
}
解释:
定义数组dp来存储不同金额时所需最少硬币数。为确定总金额是否永远也凑不出所以预先初始化数组为除dp[0]外每项都为amount+1。这样如果最后结果大于amount说明此金额是凑不出的。
案例2:爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
**注意:**给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 1:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
解决
1.确定状态:使用数组dp[i]表示i阶楼梯有dp[i]种方式爬。
2.确定状态转移公式:爬上第i层楼梯有两种方案。1.从i-1层爬一层。2.从i-2层爬两层。同理爬上i-1层也有两种方式 1. 从i-2层爬一层,2.从i-3层爬2层。故可推到出关系为dp[i]=dp[i-1]+dp[i-2]。
3.明确边界条件:当i=1时只有一种dp[1]=1。当i=2时有两种,dp[2]=2。
4.计算结果:
public int climbStairs(int n) {
int []dp=new int[n+1];
if(n==1)return 1;
if(n==2)return 2;
dp[1]=1;
dp[2]=2;
for (int i = 3; i <=n ; i++) {
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}