动态规划框架

动态规划框架

分解子问题-》找规律(状态转移方程)-》优化(自底向上)

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]);


    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值