动态规划算法

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。

利用动态规划原理解决问题的大致步骤为:

  1. 确认原问题和子问题
  2. 确认状态
  3. 确认边界状态的值
  4. 确定状态转移方程

以下是我在leetcode上刷的关于动态规划的几道习题的代码和思路

  • 爬楼梯----leetcode70
/* 爬楼梯----leetcode70
	 * 动态规划原理
	 * 	1. 确认原问题和子问题
	 * 		原问题是求 n 阶台阶所有走法的数量,子问题是求 1、2、3....n-1
	 * 		阶台阶的走法
	 * 	2. 确认状态
	 * 		本题的动态规划状态单一,第i个状态即为i阶台阶的所有走法数量
	 * 	3. 确认边界状态的值
	 * 		边界状态为1阶台阶和2阶台阶的走法,1阶台阶有1种走法,2阶台阶有2种走法,即dp[0]=1,dp[1]=2
	 * 	4. 确定状态转移方程
	 * 		将求第i 个状态的值转化为求第i-1 个状态的值和第i-2 个状态的值,
	 * 		动态规划转换方程为 :dp[i]=dp[i-1]+dp[i-2]
	 **/
	public static int climbStairs(int n) {
		int[] dp = new int[n];
		if(n <= 2) {
			return n;
		}
		dp[0] = 1;
		dp[1] = 2;
		for(int i = 2; i < n; i++) {
			dp[i] = dp[i-1] + dp[i-2];
		}
        return dp[n-1];
    }
  • 打家劫舍—leetcode198
/*
	 * 打家劫舍---leetcode198
	 * 动态规划原理
	 * 	1. 确认原问题和子问题
	 * 		原问题为n 个房间的最优解,子问题为求前1个房间、前两个房间、前n-1个房间的最优解
	 * 	2. 确认状态
	 * 		第i 个状态即为前i 能够获得的最大财宝(最优解)个房间
	 * 	3. 确认边界状态的值
	 * 		前1个房间的最优解为第1个房间的财宝
	 * 		前2个房间的最优解为第1、2个房间中财宝的较大值
	 * 	4. 确定状态转移方程
	 * 		a. 选择第i 个房间:第i 个房间 + 前i-2 个房间的最优解
	 * 		b. 选择第i-1 个房间:前i-1 个房间的最优解
	 * 		动态规划转移方程:
	 * 			dp[i] = max(dp[i-1], dp[i-2] + nums[i])
	 * */
	public static int rob(int[] nums) {
		if(nums.length == 0) {
			return 0;
		}
		if(nums.length == 1) {
			return nums[0];
		}
		if(nums.length == 2) {
			return Math.max(nums[0], nums[1]);
		}
		int[] dp = new int[nums.length];
		dp[0] = nums[0];
		dp[1] = Math.max(nums[0], nums[1]);
		for(int i = 2; i < nums.length; i++) {
			dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
		}
        return dp[nums.length-1];
    }
  • 最大子序和—leetcode53
/*
	 * 最大子序和---leetcode53
	 * 动态规划原理
	 * 	1. 确认原问题和子问题
	 * 		原问题为在数组中找到一个具有最大和的连续子数组,
	 * 		子问题为找到以原数组中每个元素结尾的子数组的最优解
	 * 	2. 确认状态
	 * 		第i 个状态即为以第i 个数字结尾的最优解
	 *	3. 确认边界状态的值
	 *		以第一个数字结尾的最大字段和dp[0]=nums[0]
	 *	4. 确定状态转移方程
	 *		a. 当dp[i-1]>0:dp[i] = dp[i-1]+nums[i]
	 *		b. 当dp[i-1]<0:dp[i] = nums[i];
	 *		dp[i] = max(dp[i-1]+nums[i], nums[i])
	 * */
	public static int maxSubArray(int[] nums) {
		if(nums.length == 0) {
			return 0;
		}
		if(nums.length == 1) {
			return nums[0];
		}
		int[] dp = new int[nums.length];
		dp[0] = nums[0];
		dp[1] = Math.max(dp[0]+nums[1], nums[1]);
		int max = dp[0];
		for(int i = 1; i < nums.length; i++) {
			dp[i] = Math.max(dp[i-1]+nums[i], nums[i]);
			if(dp[i] > max) {
				max = dp[i];
			}
 		}
		return max;
	}
  • 零钱兑换—leetcode322
/*
	 * 零钱兑换---leetcode322
	 * 动态规划
	 * 		dp[i]数组用来表示0到amount数额之间的每个值所需的最小硬币数量
	 * 		因为数组下标是从0开始的,因此dp数组的长度应该为amount+1
	 * 		设 coins为[1, 2, 5],amount=11,则dp[0]=0, dp[1]=1,dp[2]=1,dp[3]=2,dp[4]=2,
	 * 		dp[5]=1, dp[6]=2, dp[7]=2, dp[8]=3, dp[9]=3, dp[10]=2, dp[11]=3,
	 * 		因此当amount为11时的结果为3
	 * */
	public static int coinChange(int[] coins, int amount) {
		if(coins == null || coins.length == 0 || amount <= 0) {
			return 0;
		}
		int[] dp = new int[amount+1];
		for(int i = 0; i < dp.length; i++) {
			dp[i] = amount+1;
		}
		dp[0] = 0;
		for(int i = 0; i <= amount; i++) {
			for(int j = 0; j < coins.length; j++) {
				if(coins[j] <= i) {
					dp[i] = Math.min(dp[i], dp[i-coins[j]]+1);
				}
			}
		}
		return dp[amount] > amount ? -1 : dp[amount];
    }
  • 三角形最小路径和—leetcode120(经典动态规划练习题)
/*
	 * 三角形最小路径和---leetcode120(经典动态规划练习题)
	 * 	算法思路:
	 * 		1.设置一个二维数组,最优值三角形dp[][],并初始化数组元素为0,
	 * 		  dp[i][j]代表了数组三角形第i行, 第j列的最优解(从底往上推)
	 * 		2.从三角形的地面向三角形上方进行动态规划
	 * 			a. 动态规划边界条件:地面上的最优解的即为数字三角形的最后一层
	 * 			b. 利用i循环,从倒数第二层递推至正数第一层,对于每层的各列,进行动态规划递推:
	 * 			        第i行, 第j列的最优解为dp[i][j],可到达(i, j)的两个位置的最优解为
	 * 			   dp[i+1][j]、dp[i+1]dp[j+1]
	 * 			       状态转换方程为:dp[i][j]=min(dp[i+1][j], dp[i+1][j+1])+triangle[i][j]
	 * */
	 public static int minimumTotal(List<List<Integer>> triangle) {
		 if(triangle == null || triangle.size() == 0) {
			 return 0;
		 }
		 int[][] dp = new int[triangle.size()][triangle.size()];
		 for(int i = triangle.size()-1; i >= 0; i--) {
			 List<Integer> list = triangle.get(i);
			 for(int j = 0; j < list.size(); j++) {
				 dp[i][j] = Math.min(dp[i+1][j], dp[i+1][j+1])+list.get(j);
			 }
		 }
		 return dp[0][0];
	 }
  • 最长上升子序列—leetcode300
/*
	  * 最长上升子序列---leetcode300
	  * 	动态规划
	  * 		若第i个状态dp[i]代表以第i个元素结尾的最大上升子序列的长度:
	  * 		dp[i-1]代表以第i-1个元素结尾的最大上升子序列的长度
	  * */
	 public static int lengthOfLIS(int[] nums) {
		 if(nums == null || nums.length == 0) {
			 return 0;
		 }
		 int maxlen = 1;
		 int[] dp = new int[nums.length];
		 dp[0] = 1;
		 for(int i = 1; i < nums.length; i++) {
			 int max = 0;
			 for(int j = 0; j < i; j++) {
				 if(nums[i] > nums[j]) {
					 max = Math.max(max, dp[j]);
				 }
			 }
			 dp[i] = max + 1;
			 maxlen = Math.max(maxlen, dp[i]);
		 }
		 return maxlen;
	 }
  • 最小路径和—leetCode64
/*
	  * 最小路径和---leetCode64
	  * 	和三角形的最小路径和相似(但是要注意边界条件的判断)
	  * */
	 public static int minPathSum(int[][] grid) {
		 if(grid == null || grid.length == 0) {
			 return 0;
		 }
		 int[][] dp = new int[grid.length+1][grid[0].length+1];
		 for(int i = grid.length-1; i >= 0; i--) {
			 for(int j = grid[0].length - 1; j >= 0; j--) {
				if(i == grid.length - 1 && j != grid[0].length - 1)
	                dp[i][j] = grid[i][j] +  dp[i][j + 1];
	            else if(j == grid[0].length - 1 && i != grid.length - 1)
	                dp[i][j] = grid[i][j] + dp[i + 1][j];
	            else if(j != grid[0].length - 1 && i != grid.length - 1)
	                dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);
	            else
	                dp[i][j] = grid[i][j];
			 }
		 }
		 return dp[0][0];
	 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值