动态规划问题Java解题

题目:leetcode:
https://leetcode-cn.com/problems/coin-change/
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

你可以认为每种硬币的数量是无限的。

解题思路:
第一次做此类问题,学习后写的笔记,代码中解释更为直观,可见代码。该题为典型的动态规划(Dynamic Programming)问题,求最优解(最少硬币),使用动态规划来解决。
一、确定状态。
在这类问题中首先需要确定状态,可分为两点:第一点,知道完成最优解的最后一步是什么,在本题中最后一步可能为不同面额的硬币(即,如果给定为1,3,5;则最后一步就只能为1/3/5);所以可以将问题简化为:amount - 最后一步所需要的硬币数+1。设f(i)表示amount = i 时所需要的硬币数所以简化后为:f(i)=f( i - 最后一步)+1
二、转移方程。
在第一步中得到简化后的表达式,但是f( i - 最后一步)又具有很多种可能,所以可以抽象为表达式:f(i)=min{f(i-1)+1, f(i-3)+1, f(i-5)+1}
三、边界确定。
由第二步的表达式可推出,每一个i都可以被拆分为更小的子问题,但是总归会有边界,即当i=0时需要0个硬币;此外,当amount不能被拼出来时设定为需要无穷大枚硬币。
四、顺序确定。
本题由小到大来进行求解,大部分db问题也都是从小到大的顺序。

代码实现:

class Solution {
    public int coinChange(int[] coins, int amount) {
		/*设定一个数组,大小为amount+1;因为改数组用于保存各个amount最少
		需要的硬币数量,例如f[0]表示amount=0时需要的硬币数*/
        int f[] = new int[amount+1];
        /*有多少种硬币*/
        int n = coins.length;
        /*当amount=0时,一定需要0枚硬币*/
        f[0] = 0;
        /*从小到大开始计算amount为各值时(i)需要的硬币数*/
        for(int i=1; i<=amount; i++){
        	/*先将各个amount的值置为无穷大,如果存在更少的解法,则更新*/
            f[i] = Integer.MAX_VALUE;
            /*最后一步的确定。又n中可能*/
            for(int j=0; j<n; j++){
            /*边界确定,当mount=i<硬币面值时,将无法由该面值硬币组成,故跳
            过(代码体现为下标越界);当简化后的子问题无法由给定硬币组成时
            ,也跳过(代码体现为超过Integer.MAX_VALUE所规定的值)*/
                if(i>=coins[j] && f[i-coins[j]] != Integer.MAX_VALUE){
                /*判断最优解,如果简化后少于原先的方法,则更新;边界问题
                已在上一步判断*/
                    f[i] = Math.min(f[i-coins[j]]+1, f[i]); 
                }
            }
        }
		/*如果无法组成给定amount(代码体现为f[amount]=无穷大),返回-1*/
        if(f[amount] == Integer.MAX_VALUE){
            return -1;
        }else{
            return f[amount];
        }
    }
}

更新:
动态规划第二类题型:

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-paths
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

该题求左上角到右下角有多少种可能。即从(0,0)到(m-1,n-1)有多少中可能。所以可转化为:求(0,0)到(m-2,n-1)(0,0)到(m-1,n-2)共有有多少种可能,可表示为:f(m-1,n-1) = f(m-2,n-1)+f(m-1,n-2) [注:f(i,j)表示(0,0)到(i,j)的路径数]。

例如:有一个为4x7的网格:

1111111
1234567
13610152128
141020355684

网格中的数字表示为:(0,0)到该点的路径数,所以到达(m-1,n-1)的路径数为f(m-1,n-1)。

注意点:边界值:第0行的第0列的点只可能有一条路径到达,所以都为1.

代码:

class Solution {
    public int uniquePaths(int m, int n) {
    	/*初始化一个二维数组来表示该网格*/
        int f[][] = new int [m][n];
        int i,j;
        /*外部循环表示从第一行开始,到m-1行*/
        for(i=0; i<m; i++){
        	/*外部循环表示从第一列开始,到n-1列*/
            for(j=0; j<n; j++){
            	/*如果处在边界位置,路径数为1*/
                if(i==0 || j==0){
                    f[i][j] = 1;
                }else{
                	/*除边界外,每个点的路径数为(0,0)到其上面一个点的
                	路径数+(0,0)到其左边一个点的路径数,即:
                	f(m-1,n-1) = f(m-2,n-1)+f(m-1,n-2)*/
                    f[i][j] =  f[i-1][j] + f[i][j-1];
                }
            }
        }
        /*最后一个点的数值即为总路径数*/
        return f[m-1][n-1];
    }
}

再更新:
跳跃游戏:
https://leetcode-cn.com/problems/jump-game/
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。

解题思路:
该题可以有多种揭发而且可能比动态规划更优,这里只作为体现动态规划思想而用动态规划来解
首先,还是先将最终的问题拆成子问题,即到终点的前一步。假设数组长度为n,则最后为n-1;设前一步为i。所以只需要满足两点即可:一:前一点i要能够到达;二:在i点的跳跃长度要大于等于i到n-1的距离。
因此,可以推出表达式:
f[j] = f[i] && STEP[i] >= j - i

  • f [ j ] 表示第j个点能否到达
  • f [ i ]表示j的前一个点i能否到达
  • STEP[ i ]表示在第i点的最大跳跃长度
    所以表达式意味:如果一个点的前一个点能够到达,且其与下一个点的距离小于等于 STEP[ i ],则该点也一定可以到达。
    临界值:第一个点(起始位置)一定可以到达

代码实现:

class Solution {
    public boolean canJump(int[] nums) {
        int len = nums.length;
        /*表示各个点能否到达*/
        boolean f[] = new  boolean[len];
        /*起始位置一定可以到达*/
        f[0] = true;
        /*从第二个点开始判断能否到达*/
        for(int j=1; j<len; j++){
            f[j] = false;
            /*枚举该点前面的所有点是否有可以到达该点的点*/
            for(int i=0; i<j; i++){
            /*需满足两个条件*/
                if(f[i] && i+nums[i] >= j){
                    f[j] = true;
                    break;
                }
            }
        }
        return f[len-1];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值