动态规划框架
分解子问题-》找规律(状态转移方程)-》优化(自底向上)
1.从根源看动态规划的目的最多的就是求最值,例如最长递增子序列,背包问题的最大价值等等。
2.求最值需要解决的核心问题就是穷举,把所有结果列举出来,最极端的那个就是。
3.动态规划的穷举不是暴力破解,不然就不叫规划了。因为动态规划一般会存在重叠子问题,也就是会有重复的求解,我们只需要把重复的步骤用备忘录或者dp table记录下来就可以节省这部分的时间。
4.求动态规划就是求最优子结构,找出状态转移方程
1.斐波那契数列
1.1垃圾法
上学时必然写过的耗时垃圾算法,虽然看上去很简洁,但是我们把它放开就会看到时间复杂度为O(2^n)
int fib(n){
if(n==1||n==2)
return 1;
return fib(n-1)+fib(n-2);
}
1.2备忘录法
我们将重叠子问题存放在备忘录里,思想是自顶向下的,时间复杂度是O(n)
int fib(n){
//备忘录初始化
int[] num = new int[n+1];
assisted(n);
}
assisted(n){
if(n==1||n==2){return 1;}
//如果备忘录里存在就不用再查询
else if(num[n]!=0){
return num[n];
}
//备忘录赋值
num[n] = assisted(n-1)+assisted(n-2);
return num[n];
}
1.3dp数组法
采用自底向上的思想,时间复杂度也是O(n)
int fib(n){
int[] dp= new int[n+1];
dp[1] = 1;
dp[2] = 1;
for(int i = 3;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
1.4状态转移方程
状态转移方程是什么,其实就是状态转移,再拆分就是把f(n)想成一个状态,然后这个状态是f(n-1)和f(n-2)转移过来的。
动态规划其实核心就是状态转移方程,找出状态转移方程基本就做完一半
了,那剩下的一般是什么,就是优化,因为现在还是相当于暴力破解,优
化就更简单了,因为优化只有备忘录法和dp方程法。
2.凑零钱问题
给你 k 种⾯值的硬币,⾯值分别为 c1, c2 ... ck ,每种硬 币的数
量⽆限,再给⼀个总⾦额 amount ,问你最少需要⼏枚硬币凑出这个⾦
额,如果不可能凑出,算法返回 -1 。
// coins 中是可选硬币⾯值,amount 是⽬标⾦额
int coinChange(int[] coins, int amount);
2.1dp数组法
直接从从问题分析,刚开始学看到动态规划就会想这玩意好牛逼,听起来就高大上,其实就是拆分找规律。
如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币
凑够11元?
从子问题出发,要求amount最少需要几个金币。我们可以从0元开始
出发,0元最少需要0个硬币,没问题吧,然后继续向上1元最少需要几
个硬币,我们把现在已有的硬币跟需要的硬币比较一下
1.现在已有1,3,5三种面值的硬币,而我们需要1元
2.将现在已有面值遍历一遍,发现1元的适合,其他都偏大
然后开始看2元需要几个硬币,按之前的思路走一遍,需要两个一元的硬币。然后是3元,按之前思路可以直接找到3元需要的硬币数量是1.
接下来4元呢,直接可以可以借用之前求得三元的最优解,因为之前已经获得了。
所以求得状态转移方程num = min{dp[num-v]+1}
public class Coins {
private static int[] coins = {1,2,5};
// private int target = 11;
public static void main(String[] args) {
dp(11);
}
private static void dp(int target) {
/*定义数组代表余额*/
int[] array = new int[target+1];
for (int i = 0; i < target+1; i++) {
array[i] = target+1;
}
array[0] = 0;
/*自底向上求解,将每一个小于target的硬币需要最小数求出*/
for (int num = 1; num < target+1; num++) {
for (int coin:coins
) {
//如果小于0,说明这条思路不对
if ((num - coin) < 0)
continue;
//自底向上求解,所以之前的可以求解出来
array[num] = min(array[num],array[num-coin]+1);
}
}
/*判断有无最优解法*/
System.out.println(array[target]==target+1?-1:array[target]);
}
}