暴力递归到动态规划(四)

这篇具有很好参考价值的文章主要介绍了暴力递归到动态规划(四)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

暴力递归到动态规划(四)
⭐️前言⭐️

本篇文章是从暴力递归到动态规划篇目的最后一篇文章,包含了几道题目还有最终的大总结,相信这篇文章能让各位读者对动态规划有更深一步的了解。

🍉欢迎点赞 👍 收藏留言评论 📝私信必回哟😁

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

🍉博客中涉及源码及博主日常练习代码均已上传GitHub


暴力递归到动态规划(四)

🍅砍怪兽

题目:
给定3个参数,N,M,K
怪兽有N滴血,等着英雄来砍自己
英雄每一次打击,都会让怪兽流失[0~M]的血量
到底流失多少?每一次在[0~M]上等概率的获得一个值
求K次打击之后,英雄把怪兽砍死的概率

题解思路1:
总的情况数为(M+1)^k,通过暴力递归求出砍死怪兽的情况数,即可得到把怪兽砍死的概率。
递归终止的条件:
当次数为0时,看剩余血量,如果血量小于等于0,则返回1(说明是一种情况)
当血量为0时,剩余的次数都是能够砍死怪兽的情况,直接返回(M+1)^ times即为该情况下的所有情况数。
其余情况递归,根据0~M 血量的变化,进入不同的递归,记录不同的递归的情况数,求得最后的结果。

代码实现:

public class KillMonster {
    public static double right(int N,int M,int K) {
        if(N<1 || M<1 || K<1) {
            return 0;
        }
        long all=(long) Math.pow(M+1,K);
        long kill=process(K,M,N);
        return (double) ((double)kill/(double) all);
    }
    // 怪兽还剩hp点血
    // 每次的伤害范围为0~M
    // 还有times可以砍
    // 返回砍死的情况数
    public static long process(int times,int M,int hp) {
        if (times == 0) {
            return hp <= 0 ? 1 : 0;
        }
        if(hp<=0) {
            return (long) Math.pow(M+1,times);
        }
        long ways=0;
        for (int i = 0; i <=M ; i++) {
            ways+=process(times-1,M,hp-i);
        }
        return ways;
    }
}

题解思路2:
根据暴力递归来转化为动态规划解法,由于有两个可变参数,所以可以通过构建一个二维DP表来存储不同剩余次数、不同血量时的情况数,最后返回表中需要位置的结果即可。
(注意:当血量为负的时候,把怪兽砍死的结果数就是(M+1)^(times-1))

代码实现:

public class KillMonster {
    public static double dp1(int N,int M,int K) {
        if(N<1 || M<1 || K<1) {
            return 0;
        }
        long all=(long) Math.pow(M+1,K);
        long[][] dp=new long[K+1][N+1];
        dp[0][0]=1;
        for (int times = 1; times <=K ; times++) {
            dp[times][0]=(long) Math.pow(M+1,times);
            for (int hp = 1; hp <=N ; hp++) {
                long ways=0;
                for (int i = 0; i <=M ; i++) {
                    if(hp-i>=0) {
                        ways+=dp[times-1][hp-i];
                    }else {  // 当血量为负的时候,情况数直接就是下边的公式
                        ways+=(long) Math.pow(M+1,times-1);
                    }
                }
                dp[times][hp]=ways;
            }
        }
        long kill=dp[K][N];
        return (double) ((double)kill/(double) all);
    }
}

题解思路3:
由于在求每个位置的dp结果时,存在for循环的枚举依赖,所以可以通过严格依赖的表结构关系进行优化,不使用枚举依赖。

代码实现:

public class KillMonster {
    // 枚举优化
    public static double dp2(int N,int M,int K) {
        if(N<1 || M<1 ||K<1) {
            return 0;
        }
        long all=(long) Math.pow(M+1,K);
        long[][] dp=new long[K+1][N+1];
        dp[0][0]=1;
        for (int times = 1; times <=K ; times++) {
            dp[times][0]=(long) Math.pow(M+1,times);
            for (int hp = 1; hp <=N ; hp++) {
                dp[times][hp]=dp[times][hp-1]+dp[times-1][hp];
                if(hp-1-M>=0) {// 没有越界
                    dp[times][hp]-=dp[times-1][hp-1-M];
                }else {// 如果越界了说明血量为负,也得减去情况数
                    dp[times][hp]-=Math.pow(M+1,times-1);
                }
            }
        }
        long kill=dp[K][N];
        return (double) ((double)kill/(double) all);
    }
}

🍅最少货币数

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

题解思路:
从左往右的尝试模型,依次考虑每个面值对应不同数的情况,记录能够组成aim的最小张数返回即可。

代码实现:

public class MinCoinsNoLimit {
    public static int minCoins(int[] arr,int aim) {
        return process(arr,0,aim);
    }
    
    // arr[index...]面值,每种面值张数自由选择
    // 凑出正好rest的钱数,返回最小张数
    // 拿Integer.MAX_VALUE标记怎样也凑不出
    public static int process(int[] arr,int index,int rest) {
        if(index==arr.length) {
            return rest==0?0:Integer.MAX_VALUE;
        }else {
            int ans=Integer.MAX_VALUE;
            for (int pages = 0; pages*arr[index] <=rest ; pages++) {
                int next=process(arr,index+1,rest-pages*arr[index]);
                if(next!=Integer.MAX_VALUE) {
                    ans=Math.min(ans,next+pages);
                }
            }
            return ans;
        }
    }
}

题解思路2:
根据暴力递归转化为动态规划,由于有两个可变参数,所以可以依此构建一个二维dp表,记录不同面值,不同rest,再根据index+1位置面值的不同张数返回的情况数,取最小结果填入。

代码实现:

public class MinCoinsNoLimit {
    public static int dp1(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 pages = 0; pages*arr[index] <=rest ; pages++) {
                    int next=dp[index+1][rest-pages*arr[index]];
                    if(next!=Integer.MAX_VALUE) {
                        ans=Math.min(ans,pages+next);
                    }
                }
                dp[index][rest]=ans;
            }
        }
        return dp[0][aim];
    }
}

题解思路3:
由于每个位置的填写,都依赖于枚举得到的结果,所以可以根据严格依赖的表结构位置关系,优化枚举行为。
暴力递归到动态规划(四)

红色方格的值依赖于Math.min(a+0,b+1,c+2,d+3) 【最后加几取决于rest,只要不越过左边界即可】
黄色方格的值依赖于Math.min(b+0,c+1,d+2)

所以dp[index][rest]=Math.min(dp[index+1][rest],dp[index][rest-arr[index]]+1)

代码实现:

public class MinCoinsNoLimit {
    public static int dp2(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++) {
                dp[index][rest]=dp[index+1][rest];
                if(rest-arr[index]>=0&&
                dp[index][rest-arr[index]]!=Integer.MAX_VALUE) {
                    dp[index][rest]=Math.min(dp[index][rest-arr[index]]+1,dp[index][rest]);
                }
            }
        }
        return dp[0][aim];
    }
}

🍅拆分数字

题目:
给定一个正数n,求n的裂开方法数,
规定:后面的数不能比前面的数小
比如4的裂开方法有:
1+1+1+1、1+1+2、1+3、2+2、4
5种,所以返回5

题解思路1:
如果想要拆分5,就假设拆分6,先拆成了1和5,看5剩下多少种拆分办法。
base case:当rest剩余0时,说明完全拆分完成,则返回一种情况,如果pre>rest,则不符合条件,返回0。

代码实现:

public class SplitNumber {
    // n为正数
    public static int ways(int n) {
        if(n<0) {
            return 0;
        }
        if(n==1) {
            return 1;
        }
        return process(1,n);
    }

    // 上一个拆出来的数是pre
    // 还剩rest需要去拆
    // 返回拆解的方法数
    public static int process(int pre, int rest) {
        if(rest==0) {
            return 1;
        }
        if(pre>rest) {
            return 0;
        }
        int ways=0;
        for (int first = pre; first <=rest ; first++) {
            ways+=process(first,rest-first);
        }
        return ways;
    }
}

题解思路2:
由暴力递归转化为动态规划,有两个可变参数pre和rest,先跟base case完成部分位置的填写,再根据暴力递归的依赖关系,来推断出dp表中其他位置该如何填写。
暴力递归到动态规划(四)

代码实现:

public class SplitNumber {
    public static int dp1(int n) {
        if(n<0) {
            return 0;
        }
        if(n==1) {
            return 1;
        }
        int[][] dp=new int[n+1][n+1];
        for (int pre = 1; pre <=n ; pre++) {
            dp[pre][0]=1;
            dp[pre][pre]=1;
        }
        for (int pre = n-1; pre >=0 ; pre--) {
            for (int rest = pre+1; rest <=n ; rest++) {
                int ways=0;
                for (int first = pre; first <=rest ; first++) {
                    ways+=dp[first][rest-first];
                }
                dp[pre][rest]=ways;
            }
        }
        return dp[1][n];
    }
}

题解思路3:
由于每个位置的填写有枚举依赖,所以可以根据严格依赖的位置关系再做优化,对枚举行为进行优化。
dp[pre][rest]=dp[pre+1][rest]+dp[pre][rest-pre]
暴力递归到动态规划(四)

代码实现:

public class SplitNumber {
    public static int dp2(int n) {
        if(n<0) {
            return 0;
        }
        if(n==1) {
            return 1;
        }
        int[][] dp=new int[n+1][n+1];
        for (int pre = 1; pre <=n ; pre++) {
            dp[pre][0]=1;
            dp[pre][pre]=1;
        }
        for (int pre = n-1; pre >=0 ; pre--) {
            for (int rest = pre+1; rest <=n ; rest++) {
                dp[pre][rest]=dp[pre+1][rest];
                dp[pre][rest]+=dp[pre][rest-pre];
            }
        }
        return dp[1][n];
    }
}

🍅数组拆分

题目:
给定一个正数数组arr,
请把arr中所有的数分成两个集合,尽量让两个集合的累加和接近
返回最接近的情况下,较小集合的累加和

题解思路1:
从左往右的尝试模型,每个位置都有要或者不要两种情况,递归就是返回接近于rest但不大于rest的最大累加和。
代码实现:

public class SplitSumClosed {
    public static int right(int[] arr) {
        if(arr==null||arr.length<2) {
            return 0;
        }
        int sum=0;
        for(int num:arr) {
            sum+=num;
        }
        return process(arr,0,sum/2);
    }

    // arr[i...]可以自由选择,返回累加和尽量接近rest,但不能超过rest的情况下,最接近的累加和是多少
    public static int process(int[] arr,int i,int rest) {
        if(i==arr.length) {
            return 0;
        }else { // 还有数
            // 可能性1,不使用arr[i]
            int p1=process(arr,i+1,rest);
            // 可能性2,使用arr[i]
            int p2=0;
            if(arr[i]<=rest) {
                p2=arr[i]+process(arr,i+1,rest-arr[i]);
            }
            return Math.max(p1,p2);// 返回两种可能性中更接近的
        }
    }
}

题解思路2:
暴力递归中有两个可变参数,所以可以优化为一个二维dp表,来进行存储不同状态下的结果,根据位置依赖关系来填写dp表,最后返回所需要状态的结果。
代码实现:

public class SplitSumClosed {
    public static int dp(int[] arr) {
        if(arr==null||arr.length<2) {
            return 0;
        }
        int sum=0;
        for (int num:arr) {
            sum+=num;
        }
        sum/=2;
        int N=arr.length;
        int[][] dp=new int[N+1][sum+1];
        for (int i = N-1; i >=0 ; i--) {
            for (int rest = 0; rest <=N ; rest++) {
                // 可能性1,不使用arr[i]
                int p1=dp[i+1][rest];
                // 可能性2,使用arr[i]
                int p2=0;
                if(arr[i]<=rest) {
                    p2=arr[i]+dp[i+1][rest-arr[i]];
                }
                dp[i][rest]= Math.max(p1,p2);
            }
        }
        return dp[0][sum];
    }
}

🍅数组拆分2

题目:
给定一个正数数组arr,请把arr中所有的数分成两个集合
如果arr长度为偶数,两个集合包含数的个数要一样多
如果arr长度为奇数,两个集合包含数的个数必须只差一个
请尽量让两个集合的累加和接近
返回最接近的情况下,较小集合的累加和

题解思路1:
与数组拆分1类似,只不过加上了个数限制,要根据arr长度来决定集合中只能选几个数。
代码实现:

public class SplitSumClosedSizeHalf {
    public static int right(int[] arr) {
        if(arr==null||arr.length<2) {
            return 0;
        }
        int sum=0;
        for (int num:arr) {
            sum+=num;
        }
        if((arr.length&1)==0) {
            return process(arr,0,arr.length/2,sum/2);
        }else {
            return Math.max(process(arr,0,arr.length/2,sum/2),process(arr,0,arr.length/2+1,sum/2));
        }
    }
    // arr[i...]自由选择,挑选的个数一定是picks个,累加和<=rest,离rest最接近的返回
    public static int process(int[] arr, int i, int picks, int rest) {
        if(i==arr.length) {
            return picks==0?0:-1;  // 不合法用-1标记
        }else {
            // 不用arr[i]
            int p1=process(arr, i+1, picks, rest);
            // 用arr[i]
            int p2=-1;
            int next=-1;
            if(arr[i]<=rest) {
                next=process(arr,i+1,picks-1,rest-arr[i]);
            }
            // 如果下一层有效
            if(next!=-1) {
                p2=arr[i]+next;
            }
            return Math.max(p1,p2);
        }
    }
}

题解思路2:
根据暴力递归的三个可变参数,建立一个三维的dp表,根据位置依赖关系填写dp表,最后返回所要位置的dp结果。
代码实现:

public class SplitSumClosedSizeHalf {
    public static int dp(int[] arr) {
        if(arr==null||arr.length<2) {
            return 0;
        }
        int sum=0;
        for (int num:arr) {
            sum+=num;
        }
        sum/=2;
        int N=arr.length;
        int M=(N+1)/2;// 向上取整
        int[][][] dp=new int[N+1][M+1][sum+1];
        for (int i = 0; i <= N; i++) {
            for (int j = 0; j <= M; j++) {
                for (int k = 0; k <= sum; k++) {
                    dp[i][j][k] = -1;
                }
            }
        }
        for (int rest = 0; rest <=sum ; rest++) {
            dp[N][0][rest]=0;
        }
        for (int i = N-1; i >=0 ; i--) {
            for (int picks = 0; picks <=M ; picks++) {
                for (int rest = 0; rest <=sum ; rest++) {
                    // 不用arr[i]
                    int p1=dp[i+1][picks][rest];
                    // 用arr[i]
                    int p2=-1;
                    int next=-1;
                    if(arr[i]<=rest) {
                        next=dp[i+1][picks-1][rest-arr[i]];
                    }
                    // 如果下一层有效
                    if(next!=-1) {
                        p2=arr[i]+next;
                    }
                    dp[i][picks][rest]= Math.max(p1,p2);
                }
            }
        }
        if((arr.length&1)==0) {
            return dp[0][arr.length/2][sum];
        }else {
            return Math.max(dp[0][arr.length/2][sum],dp[0][arr.length/2+1][sum]);
        }
    }
}

🍅贴纸拼词

题目:
给定一个字符串str,给定一个字符串类型的数组arr。
arr里的每一个字符串,代表一张贴纸,你可以把单个字符剪开使用,目的是拼出str来。
返回需要至少多少张贴纸可以完成这个任务。
例子:str= “babac”,arr = {“ba”,“c”,“abcd”}
至少需要两张贴纸"ba"和"abcd",因为使用这两张贴纸,把每一个字符单独剪开,含有2个a、2个b、1个c。是可以拼出str的。所以返回2。
https://leetcode-cn.com/problems/stickers-to-spell-word/

题解思路:
每张贴纸都可以作为第一张,如果该贴纸有效(即可以减掉部分字符),则递归进入下一层(传入的参数变为减后的字符串);如果该贴纸无效,则直接就是默认为系统最大值,最后返回记录的最小结果。

代码实现:

public class StickersToSpellWord {
    public static int minSticker1(String[] stickers,String target) {
        int ans=process(stickers,target);
        return ans==Integer.MAX_VALUE?-1:ans;
    }

    // 所有贴纸stickers,每一种贴纸都有无穷张
    // target
    // 最少张数
    public static int process(String[] stickers,String target) {
        if(target.length()==0) {
            return 0;
        }
        int min=Integer.MAX_VALUE;
        for (String first:stickers) {
            String rest=minus(target,first);
            if(rest.length()!=target.length()) {
                min=Math.min(min,process(stickers,rest));
            }
        }
        return min+(min==Integer.MAX_VALUE?0:1);
    }

    public static String minus(String s1,String s2) {
        char[] str1=s1.toCharArray();
        char[] str2=s2.toCharArray();
        int[] count=new int[26];
        for (char ch:str1) {
            count[ch-'a']++;
        }
        for (char ch:str2) {
            count[ch-'a']--;
        }
        StringBuilder builder=new StringBuilder();
        for (int i = 0; i < 26; i++) {
            if(count[i]>0) {
                for (int j = 0; j < count[i]; j++) {
                    builder.append((char) (i+'a'));
                }
            }
        }
        return builder.toString();
    }
}

题解思路2:
可以将词频表和贴纸都转换为数组,在相减时可以直接数组相减。

代码实现:

public class StickersToSpellWord {
    public static int minStickers2(String[] stickers,String target) {
        int N=stickers.length;
        // 关键优化(用词频表代替贴纸数组)
        int[][] counts=new int[N][26];
        for (int i = 0; i < N; i++) {
            char[] str=stickers[i].toCharArray();
            for (char ch:str) {
                counts[i][ch-'a']++;
            }
        }
        int ans=process2(counts,target);
        return ans==Integer.MAX_VALUE?-1:ans;
    }
    
    // stickers[i]数组
    // 每种贴纸都有无穷张
    // 返回搞定target的最少张数
    public static int process2(int[][] stickers,String t) {
        if(t.length()==0) {
            return 0;
        }
        // target做出词频统计
        char[] target=t.toCharArray();
        int[] tcounts=new int[26];
        for (char ch:target) {
            tcounts[ch-'a']++;
        }
        int N=stickers.length;
        int min=Integer.MAX_VALUE;
        for (int i = 0; i < N; i++) {
            // 尝试第一张贴纸是谁
            int[] sticker=stickers[i];
            // 剪枝优化
            if(sticker[target[0]-'a']>0) {  // 如果目标target中的字符在该贴纸中存在
                StringBuilder builder=new StringBuilder();
                for (int j = 0; j < 26; j++) {
                    if(tcounts[j]>0) {
                        int nums=tcounts[j]-sticker[j];
                        for (int k = 0; k < nums; k++) {
                            builder.append((char) j+'a');
                        }
                    }
                }
                String rest=builder.toString();
                min=Math.min(min,process2(stickers,rest));
            }
        }
        return min+(min==Integer.MAX_VALUE?0:1);
    }
}

🍅总结

暴力递归到动态规划(四)
什么暴力递归可以继续优化?
有重复调用同一个子问题的解,这种递归可以优化;
如果每个子问题都是不同的解,无法优化也不用优化。

暴力递归和动态规划的关系
某一个暴力递归,有解的重复调用,就可以把这个暴力递归优化成动态规划
任何动态规划问题,都一定对应着某一个有解的重复调用的暴力递归
但不是所有的暴力递归,都一定对应着动态规划

常见的四种模型
1、从左往右的尝试模型
2、范围尝试模型
3、样本对应模型
4、业务限制模型


⭐️最后的话⭐️
总结不易,希望uu们不要吝啬你们的👍哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正😁

暴力递归到动态规划(四)文章来源地址https://www.toymoban.com/news/detail-480249.html

到了这里,关于暴力递归到动态规划(四)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法27:从暴力递归到动态规划(2)

    上一题比较简单,下面来一道比较难的题目。 假设有排成一行的 N 个位置,记为 1~ N , N 一定大于或等于 2 开始时机器人在其中的 M 位置上 ( M 一定是 1~ N 中的一个 ) 如果机器人来到 1 位置,那么下一步只能往右来到 2 位置; 如果机器人来到 N 位置,那么下一步只能往左来到

    2024年02月09日
    浏览(38)
  • 算法|9.从暴力递归到动态规划2

    题意:规定1和A对应、2和B对应、3和C对应…26和Z对应,那么一个数字字符串比如\\\"111”就可以转化为:“AAA”、“KA\\\"和\\\"AK” 给定一个只有数字字符组成的字符串str,返回有多少种转化结果 解题思路: 边界判断1:能够不被阻挡的走到最后,说明这个决策正确,返回1 边界判断2:0不

    2024年02月03日
    浏览(41)
  • 算法8.从暴力递归到动态规划1

    目前感觉,背包问题和货币数组问题本质相同,货币的与dp相关的三种代码写完了,快复习不完了,背包暂时先不写了,回头再写,补充,再总结,结合那个C++大神的文章再弄。 目前来讲,我接触到的背包问题有四种分别是01背包、完全背包、多重背包、以及部分背包。背包

    2024年02月07日
    浏览(37)
  • 一篇文章带你搞懂动态规划(由暴力递归到动态规划)

    ”动态规划“ 的过程相当于 记忆化搜索 , 即在普通 递归 的过程中用二维数组进行记忆化存储一些状态结果, 从而避免重复的计算(剪枝)。 举一个简单的例子:在 递归法 求解 斐波那契 数列的过程中, 就进行了多次的 重复计算 , 而动态规划相当于是对已经计算的状态

    2024年02月20日
    浏览(55)
  • 题解 | #上台阶#C++暴力动态规划解法,非递归

    25届想找实习求看看简历 英伟达笔试 Nvidia24秋招 英伟达嵌入式软件工程师笔试 9-26 2022-08-17-nvidia实习 我发现算法岗也不很难进啊(深度学习) 我发现算法岗也不很难进啊(深度学习) 顺丰科技 1.30校招实习招聘信息汇总 2024春招汇总 『 哨哥的校园招聘周报 』02/05 - 02/18 深圳银河创

    2024年02月21日
    浏览(38)
  • 爬楼梯问题-从暴力递归到动态规划(java)

    一个总共N 阶的楼梯(N 0) 每次只能上一阶或者两阶。问总共有多少种爬楼方式。 示例1: N = 1, 一步上去了,返回1. 示例2: N = 2时。 可以第一次上一阶,再上一阶,这是一种方式, 也可以一次直接上两阶,这也是一种方式, 返回 2; 示例3: N = 3: 可以选择, 1 1 1, 1

    2024年02月10日
    浏览(36)
  • 【LeetCode:72. 编辑距离 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月06日
    浏览(67)
  • 【LeetCode:64. 最小路径和 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月05日
    浏览(45)
  • 零钱兑换,凑零钱问题,从暴力递归到动态规划(java)

    322 零钱兑换 - 可以打开链接测试 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。 示例 1:

    2024年02月07日
    浏览(38)
  • 【LeetCode: 44. 通配符匹配 | 暴力递归=>记忆化搜索=>动态规划 】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域优质创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月06日
    浏览(89)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包