算法题——动态规划问题

  • 常用的数据结构:数组、链表、栈、队列、哈希表、树、图等的基本概念和实现;

  • 常用的算法:DFS / BFS、最短路径算法(Dijkstra)、贪心算法、动态规划、蓄水池算法、Manacher 算法、字符串匹配算法等;

动态规划算法

  • 动态规划(Dynamic programming,简称DP),核心思想是把原问题分解成子问题进行求解。

    与分治法不同,分治法通常利用递归求解,将分解后的子问题看作独立的,通常会反复求解公共子问题。
    动态规划通常利用迭代法自底向上求解,或者有记忆的递归法。分解后的子问题认为有重叠部分,将结果保存起来,每个子问题只求解一次。
    递归:递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。
    迭代:重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。

  • 适用场景:动态规划方法通常用来求解最优化问题

  • 使用动态规划的问题应该具备:最优子结构(原问题的最优解包含子问题的最优解,先求得子问题的最优解然后求原问题最优解);子问题重叠(子问题再分解成子问题,子问题之间存在重叠的情况)

  • 动态规划问题自底向上求解;贪心策略是先选择当时(局部)最好的选择

  • 举例:斐波那契数列、矩阵链乘法(代价最小的组合方法)、最大公共子序列LCS、背包问题。

  • 过程:划分子问题、表示子问题、状态转移(父问题如何由子问题推导)、确定边界(初始状态,最小子问题,最终状态)

  • 实现:自底向上、自顶向下

    • 自底向上:根据初始状态,逐步推导到最终状态,而这个转移的过程,必定是一个拓扑序。例如线性模型、区间模型。经典实现是斐波那契数列。
    • 自顶向下:从最终状态出发,如果遇到一个子问题还未求解,那么就先求解子问题。如果子问题已经求解,那么直接使用子问题的解,所以自顶向下动态规划又有一个形象生动的名字,叫做记忆化搜索,一般我们采用递归的方式进行求解。一般用在树上面,因为根据父亲结点,很容易找到所有的子问题,也就是所有的子结点;而自底向上的话,要去统计这个结点的所有兄弟结点是否已经实现。会稍微复杂一点,而且比较难理解。

【参考】https://baijiahao.baidu.com/s?id=1631319141857419948&wfr=spider&for=pc

1 背包问题

给定一个数组 arr,和一个整数 aim。如果可以任意选择 arr 中的数字,能不能累加得到 aim,返回 true 或者 false。

递归
  • 每个位置的数可以选择相加或不相加,用f[i][sum]表示到第i个数字时的累加值是sum,即f[1][sum]两种情况,加第一个数或不加第一个数。然后每个节点再分两种情况,可以用二叉树表示。
    在这里插入图片描述
  • 测试用例:arr={3,2,5},aim=7.
  • f[3][7]在不加第一个数,加第二个数,加第三个数的路径上
public class IsAimByRecursion {
 
    public static boolean isAim(int[] arr, int aim){
        if(arr == null || arr.length < 1){
            return false;
        }
        return process(arr, 0, 0, aim);
    }
 
 	//sum表示下标i之前的数字的决策累加和(不包括a[i])
    public static boolean process(int[] arr, int i, int sum, int aim){
        // 递归终止条件,到数组最后一个数时,sum是否等于aim
        if(i == arr.length){
            return sum == aim;
        }
        
        // 表示当前数字没有被加上,开始下一个数字(第一轮a[0]不加上,i+1开始下一次递归决定a[1])
        return process(arr, i + 1, sum, aim) 
        		// 表示当前数字被加上,开始下一个数字
        		// 最后有一个等于aim,一路往上都返回true
        		|| process(arr, i + 1, sum + arr[i], aim);
    }
}
动态规划
	public static boolean isAimByDP(int target,int[] arrs){
		
		int sum = 0;
		for(int i = 0; i < arrs.length; i++){
			sum += arrs[i];
		}
		//所有值加起来都没有目标值大,直接返回false
		if(target > sum)
			return false;

		boolean[][] dp = new boolean[arrs.length+1][sum+1];

		//将最后一行已经知道结果的填充进去
		for (int j = 0; j <= sum; j++) {
	            dp[arrs.length][j] = j==target;
	    }
	        
		for(int i = arrs.length - 1; i >= 0; i--){
			for(int j = 0; j <= sum; j++){
				if(j + arrs[i] <= sum){
					dp[i][j] = dp[i+1][j] || dp[i+1][j+arrs[i]];
				}
			}
		}
		return dp[0][0];
	}
2 矩阵最小路径问题

给你一个二维数组,二维数组中的每个数都是正数,要求从左上角走到右下角,每一步只能向右或者向下。沿途经过的数字要累加起来。返回最小的路径和。

递归
  • 从左上角开始,递归地向右或向下找最小路径。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值