算法8.从暴力递归到动态规划1

news2024/11/28 8:45:04

算法|8.从暴力递归到动态规划1

目前感觉,背包问题和货币数组问题本质相同,货币的与dp相关的三种代码写完了,快复习不完了,背包暂时先不写了,回头再写,补充,再总结,结合那个C++大神的文章再弄。

背包类问题

目前来讲,我接触到的背包问题有四种分别是01背包、完全背包、多重背包、以及部分背包。背包问题都属于NP问题(非直接求解问题),前三种一般使用动态规划进行求解,后一种一般使用贪心求解。

01背包

题意:给定两个长度都为N的数组weights和values,weights[i]和values[i]分别代表 i号物品的重量和价值。给定一个正数bag,表示一个载重bag的袋子,装的物品不能超过这个重量。返回能装下的最大价值

解题思路:

  • 先写出递归版本,然后对照着改dp。
  • 要与不要。结果越大越好,是正向决策,同时使用的累加。
  • 参数设置:重量数组w、价值数组v、当前决策到的坐标index、背包剩余的空间rest
  • 可变参数为index和rest,所以dp表是一张二维表。

核心代码:

递归代码:

public static int maxValue(int[] w, int[] v, int bag) {
    if(w==null||v==null||w.length!=v.length||w.length==0){
        return 0;
    }
    return process(w,v,0,bag);
}

public static int process(int[] w, int[] v, int index, int rest) {
    if(rest<0){
        return -1;
    }
    if(index==w.length){
        return 0;
    }
    int p1=process(w,v,index+1,rest);
    int p2=0;
    int next=process(w,v,index+1,rest-w[index]);
    if(next!=-1){
        p2=v[index]+next;
    }
    return Math.max(p1,p2);
}

dp代码:

public static int dp(int[] w, int[] v, int bag) {
    if (w == null || v == null || w.length != v.length || w.length == 0) {
        return 0;
    }
    int N=w.length;
    int[][] dp=new int[N+1][bag+1];
    for (int index = N-1; index >=0 ; index--) {
        for (int rest = 0; rest <=bag ; rest++) {
            int p1=dp[index+1][rest];
            int p2=0;
            int next=rest-w[index]<0?-1:dp[index+1][rest-w[index]];
            if(next!=-1){
                p2=v[index]+next;
            }
            dp[index][rest]=Math.max(p1,p2);
        }
    }
    return dp[0][bag];
}

测试代码:

public static void main(String[] args) {
    int[] weights = { 3, 2, 4, 7, 3, 1, 7 };
    int[] values = { 5, 6, 3, 19, 12, 4, 2 };
    int bag = 15;
    System.out.println(maxValue(weights, values, bag));
    System.out.println(dp(weights, values, bag));
}

测试结果:![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzwOIHjQ-1685191465956)(F:\typora插图\image-20230527182702030.png)]]

完全背包

题意:有 N种物品和一个容量为C的背包,每种物品都有无限件。第 i件物品的体积是 v[i],价值是w[i] .求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

解题思路:

核心代码:

测试代码:

测试结果:

多重背包

题意:有 N种物品和一个容量为C的背包,数量为s[i]。第 i件物品的体积是 v[i],价值是w[i] .求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

解题思路:

核心代码:

测试代码:

测试结果:

货币类问题

组成aim的方法数(每张认为是一种)

题意:arr是货币数组,其中的值都是正数。再给定一个正数aim。
每个值都认为是一张货币,即便是值相同的货币也认为每一张都是不同的,
返回组成aim的方法数。例如:arr = {1,1,1},aim = 2。
第0个和第1个能组成2,第1个和第2个能组成2,第0个和第2个能组成2
一共就3种方法,所以返回3

解题思路:

  • 这一题其实和01背包问题很像,只是这个是要求正好组成aim,01背包则是不超过的方法数
  • 所以这里我们只需要在aim=0时返回1,总金额超过了|根本就组不成(钱不够)就返回0
  • 注意:改写过程中三目操作符建议加上括号(血淋淋的教训…)// dp[index][rest] = dp[index + 1][rest] + (rest - arr[index]) < 0 ? 0 : dp[index + 1][rest - arr[index]];

核心代码:

暴力递归:

public static int coinWays(int[] arr, int aim) {
    return process(arr, 0, aim);
}

public static int process(int[] arr, int index, int rest) {
    if (rest < 0) {
        return 0;
    }
    if (index == arr.length) {
        return rest == 0 ? 1 : 0;
    }
    return process(arr, index + 1, rest - arr[index])
            + process(arr, index + 1, rest);
}

dp:

    public static int dp(int[] arr, int aim) {
        if (aim == 0) {
            return 1;
        }
        int N = arr.length;
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 1;
        for (int index = N - 1; index >= 0; index--) {
            for (int rest = 0; rest <= aim; rest++) {
//                dp[index][rest] = dp[index + 1][rest] + (rest - arr[index]) < 0 ? 0 : dp[index + 1][rest - arr[index]];
                dp[index][rest] = dp[index + 1][rest] + (rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : 0);
            }
        }
        return dp[0][aim];
    }

测试代码:

测试结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JD6dvJBd-1685191465957)(F:\typora插图\image-20230527191000350.png)]

组成aim的方法数(每种张数无限)

题意:arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。
每个值都认为是一种面值,且认为张数是无限的。返回组成aim的方法数
例如:arr = {1,2},aim = 4 方法如下:1+1+1+1、1+1+2、2+2
一共就3种方法,所以返回3。

解题思路:

  • 大体思路和上边相同,只不过子过程需要对要用多少张数进行遍历
  • 张数遍历时循环条件为zhang * arr[index] <= rest,对应的dp改写中也需要遍历(如果不优化的,优化之后再说,这里应该是可以进行斜率优化)

核心代码:

递归:

public static int coinsWay(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0;
    }
    return process(arr, 0, aim);
}

public static int process(int[] arr, int index, int rest) {
    if(rest<0){
        return 0;
    }
    if(index==arr.length){
        return rest==0?1:0;
    }
    int ways=0;
    for (int zhang = 0; zhang*arr[index] < rest ; zhang++) {
        ways+=process(arr,index+1,rest-zhang*arr[index]);
    }
    return ways;
}

dp:

public static int dp(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0;
    }
    int N = arr.length;
    int[][] dp = new int[N + 1][aim + 1];
    dp[N][0] = 1;
    for (int index = N - 1; index >= 0; index--) {
        for (int rest = 0; rest <= aim; rest++) {
            int ways=0;
            for (int zhang = 0; zhang*arr[index] < rest ; zhang++) {
                ways+=(rest-zhang*arr[index]<0)? 0: dp[index+1][rest-zhang*arr[index]];
            }
            dp[index][rest]=ways;
        }
    }
    return dp[0][aim];
}

测试代码:

测试结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x4inOUea-1685191465958)(F:\typora插图\image-20230527192430185.png)]

组成aim的方法数(每种张数有限)

题意:arr是货币数组,其中的值都是正数。再给定一个正数aim。每个值都认为是一张货币,认为值相同的货币没有任何不同,返回组成aim的方法数

例如:arr = {1,2,1,1,2,1,2},aim = 4方法:1+1+1+1、1+1+2、2+2
一共就3种方法,所以返回3

解题思路:

  • 本题思路与上题类似,只是张数变成有限的了,对应的遍历张数的条件多了一个
  • 另外,本题不是给两个数组一个张数组和值数组,所以我们还需要对数据进行预处理,封装,并进行数据统计,并提供对应方法让外部调用。
  • 封装构造方法初始化大小确定(我们给的);getInfo是我们进行的词频统计,根据arr,涉及到containKey,put等方法

核心代码:

递归代码:

public static class Info {
    private int[] coins;
    private int[] zhangs;

    public Info(int[] coins, int[] zhangs) {
        this.coins = coins;
        this.zhangs = zhangs;
    }
}

public static Info getInfo(int[] arr) {
    HashMap<Integer, Integer> map = new HashMap<>();
    for (int num : arr) {
        if (map.containsKey(num)) {
            map.put(num, map.get(num) + 1);
        } else {
            map.put(num, 1);
        }
    }
    int N = map.size();
    int[] coins = new int[N];
    int[] zhangs = new int[N];
    int index = 0;
    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
        coins[index] = entry.getKey();
        zhangs[index] = entry.getValue();
    }
    Info info = new Info(coins, zhangs);
    return info;
}


public static int coinsWay(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0;
    }
    Info info = getInfo(arr);
    return process(info.coins, info.zhangs, 0, aim);
}

public static int process(int[] coins, int[] zhangs, int index, int rest) {
    if (rest < 0) {
        return 0;
    }
    if (index == coins.length) {
        return rest == 0 ? 1 : 0;
    }
    int ways = 0;
    for (int zhang = 0; zhang < zhangs.length && (zhang * coins[index] < rest); zhang++) {
        ways += process(coins, zhangs, index + 1, rest - zhang * coins[index]);
    }
    return ways;
}

dp代码:

public static int dp(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return 0;
    }
    Info info = getInfo(arr);
    int[] coins = info.coins;
    int[] zhangs = info.zhangs;
    int N = coins.length;
    int[][] dp = new int[N + 1][aim + 1];
    dp[N][0] = 1;
    for (int index = N - 1; index >= 0; index--) {
        for (int rest = 0; rest <= aim; rest++) {
            int ways = 0;
            for (int zhang = 0; zhang < zhangs.length && (zhang * coins[index] < rest); zhang++) {
                ways += (rest - zhang * coins[index] < 0) ? 0 : dp[index + 1][rest - zhang * coins[index]];
            }
            dp[index][rest] = ways;
        }
    }
    return dp[0][aim];
}

测试代码:略

测试结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRNSP5h9-1685191465958)(F:\typora插图\image-20230527194435064.png)]

组成aim的最小货币数(每张张数无限)

题意:arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。
每个值都认为是一种面值,且认为张数是无限的。返回组成aim的最少货币数。

解题思路:

  • 还是需要对张数进行遍历,只不过只有一个条件
  • 接受结果值设为整数最大值,最终结果返回较小值
  • 另外另外,边界条件不满足条件的值需要修改成最大值,不难咱们得犯大难了,遭老罪了!要不然你计算的时候把没有组成aim的,但是张数更少的算里边了,肯定错啊;rest==0时,不需要货币,即使满足也不需要了,所以记得改成0[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcH6EvSS-1685191465959)(F:\typora插图\image-20230527201457956.png)]

补充:

  • 这里其实可以对数组按照货币值进行预处理/排序(使用迭代给sort传比较器//优先级队列)
  • 面值数组,不需要预处理,只需要用于降序排列的比较器

核心代码:

递归代码:

public static int minCoins(int[] arr, int aim) {
    if(aim==0){
        return 0;
    }
    if (arr == null || arr.length == 0 || aim < 0) {
        return Integer.MAX_VALUE;
    }
    return process(arr, 0, aim);
}

public static int process(int[] arr, int index, int rest) {
    if (rest < 0) {
        return Integer.MAX_VALUE;
    }
    if (index == arr.length) {
        return rest == 0 ? 0 : Integer.MAX_VALUE;
    }
    int ans = Integer.MAX_VALUE;
    for (int zhang = 0; zhang * arr[index] < rest; zhang++) {
        int next = process(arr, index + 1, rest - zhang * arr[index]);
        if (next != Integer.MAX_VALUE && next > 0) {//要不然最大值加最大值可能滚成负数
            ans = Math.min(next, ans);
        }
    }
    return ans;
}

dp代码:

public static int dp(int[] arr, int aim) {
    if (aim == 0) {
        return 0;
    }
    int N = arr.length;
    int[][] dp = new int[N + 1][aim + 1];
    dp[N][0] = 0;
    for (int j = 1; j <= aim; j++) {
        dp[N][j] = Integer.MAX_VALUE;
    }
    for (int index = N - 1; index >= 0; index--) {
        for (int rest = 0; rest <= aim; rest++) {
            int ans = Integer.MAX_VALUE;
            for (int zhang = 0; zhang * arr[index] < rest; zhang++) {
                int next = (rest - zhang * arr[index] < 0) ? Integer.MAX_VALUE : dp[index + 1][rest - zhang * arr[index]];
                if (next != Integer.MAX_VALUE && next > 0) {//要不然最大值加最大值可能滚成负数
                    ans = Math.min(next, ans);
                }
            }
            dp[index][rest]=ans;
        }
    }
    return dp[0][aim];
}

测试代码:略

测试结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9WJIFad-1685191465959)(F:\typora插图\image-20230527201505577.png)]

从左到右尝试模型总结1

改写规则:

  • 确定可变参数个数——dp是几维表
  • 确定可变参数的变化范围——是0N还是0N-1
  • 预处理(边界条件)
  • 确定普遍位置怎么确定
  • 边界判断——使用三目时一定要注意加括号😥
  • 四个角中的哪个是最终结果

例题总结:

  • 01背包——边界判断:超重是-1,没装够/刚好是0;要和不要的两种情况pk,要较小值
  • 完全背包
  • 多重背包
  • 组成aim的方法数(每张认为是一种)——边界判断:超支/不够都是0,aim=0时index<=arr.length都算是1;两种情况不需要pk,直接相加返回
  • 组成aim的方法数(每种张数无限)——边界判断:超支/不够都是0,aim=0时index<=arr.length都算是1;不是两种情况了,对有效的张数(一个条件)进行遍历,总方法相加
  • 组成aim的方法数(每种张数有限)——边界判断:超支/不够都是0,aim=0时index<=arr.length都算是1;不是两种情况了,对有效的张数(两个条件)进行遍历,总方法相加
  • 组成aim的最小货币数(每张张数无限)——边界条件判断:初值都为最大值,除了aim=0时,递归那aim=0一定在非法条件的前边,next值有效的判断;两种情况pk,要最小的

三种dp解法背包问题区别:

前三种dp解法货币数组区别:

注:这里返回的都是方法数,肯定是越多越好,不涉及边界值返回的系列问题。

  1. 货币数组类型决定了是否需要张数遍历(面值 不用)
  2. 张数有限无限决定了张数遍历的条件是1个还是两个
  3. 一般都是index倒序,rest正序

注:区分面值数组、货币数组

前者是天然去重,后者可能存在相同的,看题目设定决定是否需要进行预处理

另本类型开头的那种,其实也算是面值数组了。

两种情况了,对有效的张数(一个条件)进行遍历,总方法相加

  • 组成aim的方法数(每种张数有限)——边界判断:超支/不够都是0,aim=0时index<=arr.length都算是1;不是两种情况了,对有效的张数(两个条件)进行遍历,总方法相加
  • 组成aim的最小货币数(每张张数无限)——边界条件判断:初值都为最大值,除了aim=0时,递归那aim=0一定在非法条件的前边,next值有效的判断;两种情况pk,要最小的

三种dp解法背包问题区别:

前三种dp解法货币数组区别:

注:这里返回的都是方法数,肯定是越多越好,不涉及边界值返回的系列问题。

  1. 货币数组类型决定了是否需要张数遍历(面值 不用)
  2. 张数有限无限决定了张数遍历的条件是1个还是两个
  3. 一般都是index倒序,rest正序

注:区分面值数组、货币数组

前者是天然去重,后者可能存在相同的,看题目设定决定是否需要进行预处理

另本类型开头的那种,其实也算是面值数组了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/576921.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对KMP算法的一点碎碎念——上篇

对KMP算法的一点碎碎念——上篇 文章目录 对KMP算法的一点碎碎念——上篇1. KMP 算法 Next数组 求解问题1.1 前置知识-最长公共前后缀LCP1.1.1 前缀与后缀1.1.2 最长公共前后缀LCP 1.2 手算法求解 Next数组值(3种常见情况)1.2.1 情况1: next数组 正常存放匹配字符的长度情况1的…

前端面试-React专题

目录 一.React1. React 的主要特性是什么2.React 的核心思想是3. 什么是jsx4. render()函数5. 函数组件和类组件的区别6. HTML和React中事件处理的区别7. React 中的合成事件8. 虚拟Dom&#xff1f;为什么使用&#xff1f;如何实现&#xff1f;9. 在 constructor 中给 super 函…

Excel - 如何给单元格加上下拉框

当你使用下拉列表来限制人们在单元格中的输入时&#xff0c;数据输入会更快、更准确。当有人选择一个单元格时&#xff0c;下拉列表的向下箭头就会出现&#xff0c;可以点击它并进行选择。 创建一个下拉列表 / Create a drop-down list 你可以通过提供下拉列表使工作表更有效率…

基于微信小程序的教学质量评价系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

ubuntu下编译esp32 micropython固件编译(可自行增加模块)

目录 0. 前言1. 安装ESP-IDF2. 初始化Micropython仓库3. 选择ESP-IDF相应版本情况&#xff11;情况2 4. 开始编译5.烧录固件 0. 前言 为ESP32编译Micropython固件 操作系统&#xff1a;ubuntu22.04 1. 安装ESP-IDF 本节需要ESP-IDF环境&#xff0c;安装开发环境是必要的。 …

FPGA之手把手教你做多路信号发生器(STM32与FPGA数据互传控制波形生成)

文章目录 博主的念叨一、任务介绍1、本文目标2、设计思路3、设计注意事项 二、设计代码1.顶层文件代码2.波形生成模块3.ROM例化4.PLL例化5.引脚分配 总结 博主的念叨 博主建了一个技术资源分享的群&#xff0c;开源免费&#xff0c;欢迎进来唠嗑280730348 最近趁热打铁做了一…

pandas库的常用操作介绍

目录 1.1.Pandas概述2.Pandas索引结构3.groupby学习5.Pandas数值运算二元统计6.对象操作7.merge合并显示设置9.pivot操作10. 时间操作11.常用操作12.groupby操作13.字符串操作14.索引操作15.pandas绘图操作 1.1.Pandas概述 Python的pandas库是一个数据处理和数据分析库&#x…

javascript基础七:说说你对Javascript中作用域的理解?

一、作用域 作用域&#xff0c;即变量&#xff08;变量作用域又称上下文&#xff09;和函数生效&#xff08;能被访问&#xff09;的区域或集合 换句话说&#xff0c;作用域决定了代码区块中变量和其他资源的可见性 举个粟子 function myFunction(){let name小爱同学 } undef…

6.2:荷兰国旗问题

文章目录 实现key前面的数都小于等key&#xff0c;key后面的数都大于等于key1&#xff1a;前后指针法&#xff1a;2&#xff1a;挖坑法3&#xff1a;单指针法&#xff08;左神&#xff09; 辗转相除法求最大公约数 实现key前面的数都小于等key&#xff0c;key后面的数都大于等于…

【leetCode:剑指 Offer】20. 表示数值的字符串

1.题目描述 请实现一个函数用来判断字符串是否表示数值&#xff08;包括整数和小数&#xff09;。 数值&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格 一个 小数 或者 整数 &#xff08;可选&#xff09;一个 e 或 E &#xff0c;后面跟着一个 …

深入篇【C++】类与对象:详解内部类+匿名对象+编译器对拷贝的优化

这里写目录标题 Ⅰ.内部类【特点】1.天生友元2.直接访问static成员3.访问限制符限制4.外部类的大小 Ⅱ.匿名对象【特点】1.一行生命域2.对象具有常性3.可强行续命 Ⅲ.拷贝对象时编译器的优化 Ⅰ.内部类 概念&#xff1a;一个类定义在另一个类内部&#xff0c;这个内部的类就叫做…

【Unity100个实用小技巧】如何修改UI上材质的Shader

☀️博客主页&#xff1a;CSDN博客主页&#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2;&#x1f525;学习专栏推荐&#xff1a;面试汇总❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&#…

mysql基本操作1

库的基本操作 1.show variables like character_set_database 查看系统默认的字符集&#xff0c;若是指定数据库下使用该SQL&#xff0c;则查看的是该数据库对应的字符集。 2.show variables like collation_database 查看系统默认的字符集校验规则&#xff0c;指定数据库下使用…

Java 集合 - List 接口

文章目录 1.List 接口介绍2.List 接口常用 API3.ListIterator 迭代器4.ArrayList - 动态数组4.1 ArrayList 概述4.2 手撸动态数组 5.Vector - 动态数组6.LinkedList - 双向链表6.1 链表概述6.2 手撸双链表6.3 链表与动态数组的区别 7.Stack - 栈8.总结 1.List 接口介绍 在 Jav…

Linux:查看主机运行状态的一系列命令:top、df、iostat、sar

Linux&#xff1a;查看主机运行状态的一系列命令&#xff1a;top、df、iostat、sar 命令top监控系统资源&#xff1a; 使用top(回车)命令后&#xff0c;整个控制台会变成任务管理器的形式&#xff1a; 退出可以使用&#xff1a;ctrlc 或 q 第一行补充&#xff1a;表示正在执行的…

高程复习 欧几里得算法和扩展欧几里得算法考试前冲刺简约版

gcd(m,n)gcd(n,m%n) gcd欧几里得算法标准代码求最大公约数 #include <iostream>using namespace std;typedef long long LL; LL gcd(int a,int b) {if(b0)return a;return gcd(b,a%b); } int main() {LL a,b;cin>>a>>b;cout<<gcd(a,b)<<endl;re…

Linux基础:文件权限详细说明(全)

一、前提 我们要知道&#xff0c;Linux系统&#xff0c;一切皆文件的含义。 对于Linux来说&#xff0c;一切皆文件。 我们常涉及到的概念是目录和文件。 权限主要有三种&#xff1a;r(读)w(写)x(执行)。 二、正文 1、修改文件或者目录所属用户和所属组 chown [用户名[:组名…

规则网络构建

规则网络构建 文章目录 规则网络构建[toc]1 规则网络定义2 规则网络的构建3 代码实现 1 规则网络定义 常见规则网络包包括全局耦合网络、最近邻耦合网络和星型耦合网络&#xff0c;三种规则网络定义如下&#xff1a; (1)全局耦合网络&#xff1a;任意两个节点均存在连边的网络…

云原生 HTAP -- PolarDB-IMCI:A Cloud-Native HATP Database

文章目录 0 背景1 IMCI 架构1.1 架构演进的背景1.2 基本架构1.2 基本使用1.4 列索引存储 设计1.5 RW-RO 的数据同步实现1.5.1 CALS1.5.2 2P-COFFER 1.6 计算引擎实现1.7 性能 近期除了本职工作之外想要再跟进一下业界做讨论以及落地的事情&#xff0c;扩宽一下视野&#xff0c;…

算法7.从暴力递归到动态规划0

算法|7.从暴力递归到动态规划0 1.汉诺塔 题意&#xff1a;打印n层汉诺塔从最左边移动到最右边的全部过程 解题思路&#xff1a; 把字母抛掉&#xff0c;变成左中右三个盘子多个盘子能一下到吗&#xff1f;不能&#xff0c;把上边的拿走&#xff0c;最下边的才能放到指位置(…