动态规划---例题详解篇(入门-深入)

1.数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:

输入:n = 1
输出:["()"]

class Solution {

    public List<String> res = new ArrayList<>();

    public List<String> generateParenthesis(int n) {

        dfs(0,0,n,"");
        return res;
    }

    public void dfs(int leftCount,int rightCount,int n,String sum) {

        
        if(leftCount > n || rightCount > n || rightCount > leftCount)
        return;

        if(leftCount == n && rightCount == n) {

            res.add(sum);
            return;
        }
        
        dfs(leftCount+1,rightCount,n,sum+"(");
        dfs(leftCount,rightCount + 1,n,sum + ")");
    }
}

2.给定三个字符串 s1、s2、s3,请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。

两个字符串 s 和 t 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

s = s1 + s2 + ... + sn
t = t1 + t2 + ... + tm
|n - m| <= 1
交错 是 s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ...
注意:a + b 意味着字符串 a 和 b 连接。

 (1)使用二维数组

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {

        int n = s1.length();
        int m = s2.length();
        int p = s3.length();

        if(n + m != p)
        return false;

        //f[i][j]表示用s1的前i个字符和用s2的前i个字符是否能符合s3的前(i + j - 1)个字符
        boolean[][] f = new boolean[n + 1][m + 1];
        f[0][0] = true;

        for(int i = 0;i <= n;i++) {

            for(int j = 0;j <= m;j++) {

                int q = i + j - 1;
                if(i > 0) {

                    //f[i][j] = f[i][j] || (f[i - 1][j] && s3.charAt(q) == s1.charAt(i - 1));
                    //原题是以上的形式,速度慢了一半
                    //这里的f[i][j] = f[i][j]只是因为j要从0开始,告诉j我i能满足,已经不需要你了
                    
                    f[i][j] = (f[i - 1][j] && s3.charAt(q) == s1.charAt(i - 1));
                }

                if(j > 0) {
                    f[i][j] = f[i][j] || (f[i][j - 1] && s2.charAt(j - 1) == s3.charAt(q));
                }
            }
        }

        return f[n][m];
    }
}

(2)使用一维数组进行优化

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {
        int n = s1.length(), m = s2.length(), t = s3.length();

        if (n + m != t) {
            return false;
        }

        boolean[] f = new boolean[m + 1];

        f[0] = true;
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= m; ++j) {
                int p = i + j - 1;
                if (i > 0) {
                    f[j] = f[j] && s1.charAt(i - 1) == s3.charAt(p);
                }
                if (j > 0) {
                    f[j] = f[j] || (f[j - 1] && s2.charAt(j - 1) == s3.charAt(p));
                }
            }
        }

        return f[m];
    }
}

(3)动态规划模板

class Solution {
    public boolean isInterleave(String s1, String s2, String s3) {

        if(s1 == null || s2 == null || s3 == null)
        return false;

        char[] a = s1.toCharArray();
        char[] b = s2.toCharArray();
        char[] c = s3.toCharArray();

        if(s1.length() + s2.length() != s3.length())
        return false;

        boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1];
        dp[0][0] = true;

        for(int i = 1;i <= s1.length();i++) {

            if(s1.charAt(i - 1) != s3.charAt(i - 1))
            break;

            dp[i][0] = true;
        }

        for(int i = 1;i <= s2.length();i++) {

            if(s2.charAt(i - 1) != s3.charAt(i - 1))
            break;

            dp[0][i] = true;
        }


        for(int i = 1;i <= s1.length();i++) {

            for(int j = 1;j <= s2.length();j++) {

                if((a[i - 1] == c[i + j - 1] && dp[i -1][j]) || (b[j - 1] == c[i + j - 1] && dp[i][j - 1])) {

                    dp[i][j] = true;
                }
            }
        }

        return dp[s1.length()][s2.length()];
    }
}

3.

class Solution {
    public int maximalSquare(char[][] matrix) {

        int n = matrix.length,m = matrix[0].length;
        int max = 0;
        if(matrix == null || n == 0 || m == 0)
        return 0;

        //dp[i][j]记录的是以i,j为正方形的右下方坐标
        int[][] dp = new int[n][m];

        for(int i = 0;i < n;i++) {

            for(int j = 0;j < m;j++) {

                if(matrix[i][j] == '1') {

                    if(i == 0 || j == 0) {

                        dp[i][j] = 1;
                    }else {

                        //这里取最小值是因为比如i,j的左边有一个一,上面也有一个一,但是斜上是0,这样是不能构成正方形的,所以应该取最小值
                        dp[i][j] = Math.min(Math.min(dp[i - 1][j],dp[i][j - 1]),dp[i - 1][j - 1]) + 1;
                    }

                    max = Math.max(max,dp[i][j]);
                }
            }
        }

        return max * max;
    }
}

作者:fff999
链接:https://leetcode-cn.com/problems/maximal-square/solution/by-fff999-buj0/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

方法2(但是会超时dfs)

class Solution {

    int n,m;
    public int maximalSquare(char[][] matrix) {

        int max = 0;
        n = matrix.length;
        m = matrix[0].length;

        if(matrix == null || n == 0 || m == 0)
        return 0;

        for(int i = 0;i < n;i++) {

            for(int j = 0;j < m;j++) {

                if(matrix[i][j] == '1') {

                    max = Math.max(max,dfs(i,j,matrix));
                }
            }
        }

        return max * max;
    }

    public int dfs(int i,int j,char[][] matrix) {

        if(i < 0 || j < 0 || i >= n || j >= m) {

            return 0;
        }

        if(matrix[i][j] != '1')
        return 0;

        int res = 1;
        int a = dfs(i + 1,j,matrix);
        int b = dfs(i,j + 1,matrix);
        int c = dfs(i + 1,j + 1,matrix);

        res += Math.min(Math.min(a,b),c);

        return res;
    }
}

4.

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

class Solution {
    public int rob(int[] nums) {

        int len = nums.length;

        if(len == 1)
        return nums[0];

        int[] dp = new int[len];

        dp[0] = nums[0];
        dp[1] = Math.max(nums[0],nums[1]);

        for(int i = 2;i < len;i++) {

            dp[i] = Math.max(dp[i - 2] + nums[i],dp[i-1]);
        }

        return dp[len - 1];
    }
}

5.

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:

输入:nums = [1,2,3]
输出:3

class Solution {

//其实就是分成两种情况,每种情况的起始与结束的下标不同
    public int rob(int[] nums) {

        int len = nums.length;
        if(len == 1)
        return nums[0];
        else if(len == 2) {
            return Math.max(nums[0],nums[1]);
        }
        int[] dp1 = new int[len];
        int[] dp2 = new int[len];
         
        dp1[0] = nums[0];
        dp1[1] = Math.max(nums[0],nums[1]);

        for(int i = 2;i < len - 1;i++) {

            dp1[i] = Math.max(dp1[i - 2] + nums[i],dp1[i - 1]);
        }

        dp2[1] = nums[1];
        dp2[2] = Math.max(nums[2],nums[1]);

        for(int i = 3;i < len;i++) {

            dp2[i] = Math.max(dp2[i - 2] + nums[i],dp2[i - 1]);
        }

        return Math.max(dp1[len - 1-1],dp2[len - 1]);
    }
}

6.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {

        HashMap<TreeNode,Integer> map = new HashMap<>();
        return dp(root,map);
    }

    public int dp(TreeNode root,HashMap<TreeNode,Integer> map) {

        if(root == null)
        return 0;

        if(map.containsKey(root))
        return map.get(root);

        int val = root.val;

        if(root.left != null) {

            val += dp(root.left.right,map) + dp(root.left.left,map);
        }

        if(root.right != null) {

            val += dp(root.right.left,map) + dp(root.right.right,map);
        }

        

        int res =  Math.max(val,dp(root.left,map) + dp(root.right,map));

        map.put(root,res);

        return res;
    }
}

7.

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

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

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1
示例 2:

输入:coins = [2], amount = 3
输出:-1
示例 3:

输入:coins = [1], amount = 0
输出:0

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        int max = amount + 1;
        Arrays.fill(dp,max);
        dp[0] = 0;

        for(int i = 1;i <= amount;i++) {

            for(int j = 0;j < coins.length;j++) {

                if(i - coins[j] >= 0) {

                    dp[i] = Math.min(dp[i],dp[i - coins[j]] + 1);
                }
            }
        }

        return dp[amount] > amount ? -1 : dp[amount];
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海边的彩虹与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值