算法系列之动态规划

简介: 动态规划(Dynamic Programming,简称DP)是一种用于解决复杂问题的算法设计技术。它通过将问题分解为更小的子问题,并存储这些子问题的解来避免重复计算,从而提高算法的效率。

_20250227173818.jpg

动态规划(Dynamic Programming,简称DP)是一种用于解决复杂问题的算法设计技术。它通过将问题分解为更小的子问题,并存储这些子问题的解来避免重复计算,从而提高算法的效率。本文将介绍动态规划的基本概念、适用场景、复杂度分析,并通过Java代码实现经典的动态规划问题。

动态规划介绍

动态规划的核心思想是将一个复杂的问题分解为若干个相互重叠的子问题,通过解决这些子问题来构建原问题的解。动态规划通常适用于具有以下两个性质的问题:

  • 最优子结构:问题的最优解包含其子问题的最优解。

  • 重叠子问题:在求解过程中,相同的子问题会被多次计算。

动态规划算法的实现方式:

  • 自底向上(Bottom-Up):通过迭代的方式从最小的子问题开始,逐步构建更大的子问题的解,直到解决原问题。

复杂度分析

动态规划的时间复杂度和空间复杂度取决于问题的规模和子问题的数量。假设问题的规模为n,子问题的数量为m,则:

  • 时间复杂度:通常为O(m * n),其中m是子问题的数量,n是每个子问题的计算复杂度。

  • 空间复杂度:通常为O(m),用于存储子问题的解。

Java实现斐波那契数列

斐波那契数列:斐波那契数列的定义是从0和1开始,后续的每一项都是前两项的和,即0、1、1、2、3、5、8、13、21、34……这个数列在数学、自然界以及日常生活中都有着广泛的应用和意义。‌

我们通过动态规划和递归两种方式实现输入一个正整数 n ,输出斐波那契数列的第 n 项。

/**
 * 斐波那契数列
 *
 * 斐波那契数列:斐波那契数列的定义是从0和1开始,后续的每一项都是前两项的和,即0、1、1、2、3、5、8、13、21、34……这个数列在数学、自然界以及日常生活中都有着广泛的应用和意义。‌
 *
 * 输入一个正整数 n ,输出斐波那契数列的第 n 项。
 */
public class FibonacciSequence {
   

    /**
     *动态规划解法
     * @param n
     * @return
     */

    public static int fibonacciDP(int n) {
   
        // 边界条件
        if (n <= 1) {
   
            return n;
        }
        //定义初始发f(n-2)的值
        int prev2 = 0;
        //定义初始发f(n-1)的值
        int prev1 = 1;
        //定义初始发f(n)的值
        int current = 0;
        // 循环计算斐波那契数列的第 n 项(f(n) = f(n-1)+f(n-2))
        for (int i = 2; i <= n; i++) {
   
            current = prev1 + prev2;
            prev2 = prev1;
            prev1 = current;
        }
        return current;
    }

    /**
     *递归解法
     * @param n
     * @return
     */

    public static int fibonacciRec(int n) {
   
        // 边界条件
        if (n <= 1) {
   
            return n;
        }
        //递归求解
        return fibonacciRec(n-1) +fibonacciRec(n-2);
    }
    public static void main(String[] args) {
   
        System.out.println(fibonacciDP(10));
        System.out.println(fibonacciRec(10));
    }

}
AI 代码解读

⼯作安排问题

  • 问题描述:

⼩明每周上班都会拿到⾃⼰的⼯作清单,⼯作清单内包含 n 项⼯作,每项⼯作都有对应的耗时时⻓(单位h)和报酬,⼯作的总报酬为所有已完成⼯作的报酬之和。那么请你帮⼩明安排⼀下⼯作,保证⼩明在指定的⼯作时间内⼯作收⼊最⼤化。

  • 输入描述:

输⼊的第⼀⾏为两个正整数 T , n 。 T 代表⼯作时⻓(单位h, 0 < T < 100000 ) , n 代表⼯作数量( 1 < n < 3000 )

接下来是 n ⾏,每⾏包含两个整数 t , w 。 t 代表该项⼯作消耗的时⻓(单位h, t > 0 ) , w 代表该项⼯作的报酬。

  • 输出描述

输出⼩明指定⼯作时⻓内⼯作可获得的最⼤报酬。

  • 示例

示例1

输入:

40 3
20 10
20 20
20 5
AI 代码解读

输出:

30
AI 代码解读

示例2

输入:

100 3
50 10
20 30
50 20
AI 代码解读

输出:

50
AI 代码解读
  • 代码实现
public class WorkArrangement {
   

    public static void main(String[] args) {
   
        Scanner scanner = new Scanner(System.in);

        // 输⼊最⼤⼯作时间max_time和⼯作数量N
        int max_time = scanner.nextInt();
        // ⼯作数量N
        int N = scanner.nextInt();
        //工作时常数组
        int[] times = new int[N];
        //工资数组
        int[] wages = new int[N];

        // 输⼊N份⼯作的时间和报酬
        for (int i = 0; i < N; i++) {
   
            times[i] = scanner.nextInt();
            wages[i] = scanner.nextInt();
        }

        Map<Integer,Integer> dpMap = new HashMap<>();
        //初始化工作时长和工资
        dpMap.put(0,0);

        for(int i = 0; i< N; i++){
   
            // 复制当前的dpMap
            Map<Integer, Integer> currentDP = new HashMap<>(dpMap);
            //当前工作的时长和工资
            int time = times[i];
            int wage = wages[i];
            // 遍历当前currentDP哈希表中,所有的时间和报酬
            for(Map.Entry<Integer, Integer> entry: currentDP.entrySet()){
   
                int preTime = entry.getKey();
                int preWage = entry.getValue();
                // 当前的结束时间
                int endTime = preTime + time;
                // 当前结束时间对应的工资
                int endWage = preWage + wage;
                // 如果结束时间不超过最大时间,则更新dpMap
                if(endTime <= max_time){
   
                    dpMap.put(endTime, Math.max(dpMap.getOrDefault(endTime,0),endWage));
                }
            }

        }

        // 输出dpMap哈希表中的最⼤值
        int maxWage = 0;
        for (int wage : dpMap.values()) {
   
            maxWage = Math.max(maxWage, wage);
        }

        System.out.println(maxWage);

    }
}
AI 代码解读

总结

动态规划是一种强大的算法设计技术,适用于解决具有最优子结构和重叠子问题性质的问题。通过合理地分解问题和存储子问题的解,动态规划可以显著提高算法的效率。本文通过斐波那契数列和工作安排问题展示了动态规划的基本思想和Java实现。希望本文能帮助你更好地理解和应用动态规划算法。

目录
打赏
0
6
6
1
203
分享
相关文章
|
6月前
|
深入了解动态规划算法
深入了解动态规划算法
138 1
【动态规划算法】蓝桥杯填充问题(C/C++)
【动态规划算法】蓝桥杯填充问题(C/C++)
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
【动态规划篇】穿越算法迷雾:约瑟夫环问题的奇幻密码
【潜意识Java】蓝桥杯算法有关的动态规划求解背包问题
本文介绍了经典的0/1背包问题及其动态规划解法。
98 5
惊呆了!Python算法设计与分析,分治法、贪心、动态规划...这些你都会了吗?不会?那还不快来学!
【7月更文挑战第10天】探索编程巅峰,算法至关重要。Python以其易读性成为学习算法的首选。分治法,如归并排序,将大问题拆解;贪心算法,如找零问题,每步求局部最优;动态规划,如斐波那契数列,利用子问题解。通过示例代码,理解并掌握这些算法,提升编程技能,面对挑战更加从容。动手实践,体验算法的神奇力量吧!
102 8
|
6月前
|
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
250 2
动态规划算法学习三:0-1背包问题
|
5月前
|
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
114 2
|
6月前
|
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
127 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
6月前
|
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
274 0
动态规划算法学习二:最长公共子序列

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等