动态规划学习笔记:硬币交换、数组左上到右下最多路径、青蛙跳问题

it2026-10-02  3

动态规划

b站有讲解视频,我是看视频学的,代码自己敲得 https://www.bilibili.com/video/BV1xb411e7ww 题目特点: 基本思想: 将待求解的问题分解成若干个子问题,先求解子问题,然后从这若干个子问题(和分治区别:往往不独立)的解中得到原问题的解。有些子问题或许被重复计算多次,在动态规划中,会保存每个被计算过子问题的解,将其填入表中。

动态规划一般步骤

找出最优解的性质(将要计算的东西分成两部分,两部分都是最优解才满足整体最优),刻画其结构特征递归定义最优值自底向上计算最优值根据最优质时得到的信息,构造最优解 1-3是基本步骤,第四步可以视情况省去

实现思路

1、确定状态: 最后一步(最优策略中使用的最后一枚硬币ak); 化成子问题(最少的硬币评出更小的面值27-ak) 2、转移方程:根据子问题的定义直接得到f[X] = min{f[X-2]+1,f[X-5]+1,f[X-7]+1} 3、初始条件(用转移方程算不出来的,需要手动定义)和边界情况(不要越界) 4、计算顺序:要利用之前的计算结果

例题

Coin Change问题

分析: 这是一个求最大最小值的问题(75555) 用贪心,尽量大——7775,少一块凑不出来 ==>不对

最优策略一定是有K枚硬币a1、a2、……ak面值加起来是27,这说明一定有最后一枚硬币ak,除掉这枚硬币,前面硬币的面值加起来的值是27-ak

使用递归解

问题:重复计算很多,效率低下

从0开始算起,一次算3种,一共算27步,时间复杂度:n*m(n种硬币,m块钱)

// An highlighted block public class DynamicProgramming { /* 硬币问题,动态规划 */ public static int getMinNums(int[] coins, int money) { int[] arr = new int[money+1]; arr[0] = 0; for (int i = 1; i <= money; i++) { int min = Integer.MAX_VALUE; for (int j = 0; j < coins.length; j++) { int t1 = coins[j]; //注意这里没有判断了一下arr[i-t1]是不是最大整数,是为了防止最大整数加1越界 if (i - t1 < 0||arr[i - t1]==Integer.MAX_VALUE) { continue; } else { min = Math.min(min, arr[i - t1] + 1); } } arr[i] = min; } return arr[money]; } /* 硬币问题递归解法 */ public static int getMinNumsRecur(int[] coins, int money) { if (money == 0) { return 0; } int res = Integer.MAX_VALUE; for (int i = 0; i < coins.length; i++) { if (money >= coins[i]) { int res1 = Integer.valueOf(getMinNumsRecur(coins, money - coins[i])); if(res1!=Integer.MAX_VALUE) { res = Math.min(res, res1 + 1); } } } return res; } public static void main(String[] args) { int[] coins = {2,5,7}; int money = 27; System.out.println("动态规划解法"); System.out.println(getMinNums(coins,money)); System.out.println("递归解法"); System.out.println(getMinNumsRecur(coins,money)); } } //我笔试时遇到一种变体:在最少的基础上得到每个硬币的数量 public static int[] getRes1(int money){ int[] num = {1,5,10,20,50,100}; int[][] arr0 = new int[num.length][money+1]; int[] arr = new int[money+1]; for (int i = 0; i < num.length; i++) { arr0[i][0] = 0; } for (int i = 1; i < arr.length; i++) { int min = Integer.MAX_VALUE; for (int j = 0; j < num.length; j++) { if(i-num[j]<0||arr[i-num[j]]==Integer.MAX_VALUE){ continue; }else{ if(min>arr[i-num[j]]+1){ min = arr[i-num[j]]+1; for (int k = 0; k < num.length; k++) { arr0[k][i] = arr0[k][i-num[j]]; } arr0[j][i] = arr0[j][i]+1; } } } arr[i] = min; } int[] res = new int[num.length]; for(int i = 0;i<num.length;i++){ res[i] = arr0[i][money]; } return res; } Unique Paths问题

public static int getPathNums(int m,int n){ int[][] arr = new int[m][n]; //除了初始值,一行一行进行计算 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if(i==0||j==0){ arr[i][j] = 1;//定义初始条件,第一行和第一列只有一种方法 }else{ //当前位置的路径数等于他上面和左边位置的路径数相加 arr[i][j] = arr[i-1][j] +arr[i][j-1]; } } } return arr[m-1][n-1]; } Jump GAME问题

public static boolean jump(int [] arr){ int[] arr1 = new int[arr.length]; arr1[0] = 1; for (int i = 0; i < arr.length; i++) { int temp = arr[i]; if(arr1[i]==1){ for (int j = i+1; j <= i+temp; j++) { if(j<arr1.length&&arr1[j]==0){ arr1[j] = 1; } } } } return arr1[arr.length-1]==1; }
最新回复(0)