数据结构与算法·动态规划 (一) (Dynamic Programming)

本文详细探讨了动态规划在计数、求最值和存在性问题中的应用,包括硬币找零、路径计数、跳跃游戏等经典题型。通过步骤详解和实战代码演示,助你掌握动态规划解题技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Dynamic Programming



一,动态规划题目的特点

  • 计数型
    • 有多少种方式走到右下角
    • 有多少种方法选出K个数使得和是Sum
  • 求最大,最小值
    • 从左上角走到右下角路径的最大数字和
    • 最长上升子序列长度
  • 求存在性
    • 取石子游戏,先取是否必胜
    • 能不能选出K 个数使得和是Sum

二,动态规划解题步骤

  • 确定状态
    • 数组中的每个元素dp[i]或者dp[i][j]代表什么;数组需要开多大,是否需要加一。
    • 确定最后一步和子问题(问 题一样,规模变小)
  • 确定转移方程
  • 确定初始条件和边界情况(用转移方程算不出来的,没有意义的,需要手动定义;边界情况:注意不要数组越界)
  • 计算顺序(一般为从小到大,从上到下,从左到右)

三,习题练习

(1), 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。你可以认为每种硬币的数量是无限的。
代码如下(示例):

public int coinNumber(int[] A, int B){
         //0~n: n+1
         int[] f = new int[B+1];
         int n = A.length; //number of the available coins.
         
         //initialization
         f[0]=0;
         for(int i=1; i<=B; i++) {
             //先假设都拼不出来
             f[i]=Integer.MAX_VALUE;
             //last coin A[j]
             //f[j] = min{f[i-A[0]+1,~f[i-A[n]+1}
             for(int j=0; j<n; j++) {
                 //无穷大+1会造成越界,也就是拼不出来(i-A[j])
                 if(i-A[j]>=0 && f[i-A[j]]!=Integer.MAX_VALUE) {
                     f[i]=Math.min(f[i], f[i-A[j]]+1);
                 }
             }
         }
         
         if(f[B]==Integer.MAX_VALUE) {
             return -1;
         }
         
         return f[B];
     }

(2), 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?
请添加图片描述

public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        for(int i=0; i<m; i++){
            for(int j=0; j<n; j++){
                if(i==0 || j==0){
                    dp[i][j]=1;
                }else{
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
        }
        return dp[m-1][n-1];
    }

(3), 跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

class Solution {
    public boolean canJump(int[] nums) {
        boolean[] dp = new boolean[nums.length];
        dp[0]=true;


        for(int i=1; i<nums.length; i++){
            dp[i]=false;
            for(int j=0; j<i; j++){
                if(dp[j] && nums[j]+j>=i){
                    dp[i]=true;
                    break;
                }
            }


        }
        return dp[nums.length-1];
    }
}

(4), 跳跃游戏 II 给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。
 int length = nums.length;
        int end = 0;
        int maxPosition = 0; 
        int steps = 0;
        for (int i = 0; i < length - 1; i++) {
            maxPosition = Math.max(maxPosition, i + nums[i]); 
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }
        return steps;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值