LeetCode —— 动态规划

持续更新中.....................................

509. 斐波那契数

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1。

示例:输入:n = 2         输出:1         解释:F(2) = F(1) + F(0) = 1 + 0 = 1

class Solution {
    public int fib(int n) {
        if(n < 2){
            return n;
        }

        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        for(int i = 2; i <= n; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例:输入:n = 2         输出:2

class Solution {
    public int climbStairs(int n) {
        if(n <= 2){
            return n;
        }
        
        int[] dp = new int[n + 1];
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= n; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }

        return dp[n];
    }
}

746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。

示例:输入:cost = [1,100,1,1,1,100,1,1,100,1]         输出:6

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 0;
        for(int i = 2; i <= n; i++){
            dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[n];
    }
}

62. 不同的路径

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

示例:输入:m = 3, n = 7         输出:28

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        //初始化
        for(int i = 0; i < m; i++){
            dp[i][0] = 1;
        }
        for(int j = 0; j < n; j++){
            dp[0][j] = 1;
        }

        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
}

63. 不同的路径II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1 和 0 来表示。

示例:输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]         输出:2

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];

        for(int i = 0; i < m && obstacleGrid[i][0] == 0; i++){
            dp[i][0] = 1;
        }
        for(int j = 0; j < n && obstacleGrid[0][j] == 0; j++){
            dp[0][j] = 1;
        }

        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                if(obstacleGrid[i][j] == 0){
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }else{
                    dp[i][j] = 0;
                }
            }
        }
        return dp[m - 1][n - 1];
    }
}

343. 整数拆分

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。

示例:输入: n = 10         输出: 36         解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];   //dp[i] 为正整数 i 拆分后的结果的最大乘积
        dp[2] = 1;
        for(int i = 3; i <= n; i++){
            for(int j = 1; j <= i - j; j++){
                // (i - j) * j 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘
                // dp[i - j] * j 是将 i 拆分成两个以及两个以上的个数,再相乘。
                dp[i] = Math.max(dp[i], Math.max((i - j) * j, dp[i - j] * j));
            }
        }
        return dp[n];
    }
}

96. 不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n + 1];
        //初始化0个节点和1个节点的情况
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2; i <= n; i++){
            for(int j = 1; j <= i; j++){
                //对于第i个节点,需要考虑 1 作为根节点直到 i 作为根节点的情况,所以需要累加
                //一共i个节点,对于根节点 j 时,左子树的节点个数为j-1,右子树的节点个数为i-j
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
}

01背包

有n件物品和一个最多能背重量为bagSize的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,问背包能背的物品最大价值是多少?

示例:输入:weight = [1, 3, 4]; value = [15, 20, 30]; bagsize = 4;         输出:35

class Solution{
    public int bagProblem(int[] weight, int[] value, int bagSize) {
        int[][] dp = new int[weight.length][bagSize + 1];

        // 初始化dp数组
        for(int j = weight[0]; j <= bagSize; j++) {
            dp[0][j] = value[0];
        }

        for(int i = 1; i < weight.length; i++){
            for(int j = 1; j <= bagSize; j++){
                if(j < weight[i]){
                    dp[i][j] = dp[i - 1][j];  //当前背包的容量都没有当前物品i大的时候,则不放物品i
                }else{
                    //比较 不放物品i 和 放物品i 这两种情况下,哪种背包中物品的价值最大
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }
        return dp[weight.length - 1][bagSize];
    }
}
class Solution{
    public int bagProblem(int[] weight, int[] value, int bagSize) {
        int[] dp = new int[bagSize + 1];  //dp[j]表示背包容量为j时,能获得的最大价值
        
        for(int i = 0; i < weight.length; i++){
            for(int j = bagSize; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        return dp[bagSize];
    }
}

416. 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例:输入:nums = [1,5,11,5]         输出:true

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = Arrays.stream(nums).sum();
        if(sum % 2 != 0){
            return false;
        }

        //转化为01背包问题:选取的数字的和恰好等于整个数组的元素和的一半
        int target = sum / 2;
        boolean[][] dp = new boolean[nums.length][target + 1];

        //初始化dp
        if(nums[0] <= target){
            dp[0][nums[0]] = true;
        }

        for(int i = 1; i < nums.length; i++){
            for(int j = 1; j <= target; j++){
                if(j < nums[i]){
                    dp[i][j] = dp[i - 1][j];
                }else{
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }
        }
        return dp[nums.length - 1][target];
    }
}
class Solution {
    public boolean canPartition(int[] nums) {
        int sum = Arrays.stream(nums).sum();
        if(sum % 2 != 0){
            return false;
        }

        //转化为01背包问题:选取的数字的和恰好等于整个数组的元素和的一半
        int target = sum / 2;
        boolean[] dp = new boolean[target + 1];

        //初始化dp
        if(nums[0] <= target){
            dp[nums[0]] = true;
        }

        for(int i = 1; i < nums.length; i++){
            for(int j = target; j >= nums[i]; j--){
                dp[j] = dp[j] || dp[j - nums[i]];
            }
        }
        return dp[target];
    }
}

1049. 最后一块石头的重量

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。

示例:输入:stones = [2,7,4,1,8,1]         输出:1

class Solution {
    public int lastStoneWeightII(int[] stones) {
        //石头的总重量为sum, ki=-1的石头的重量之和为neg,则其余ki=1的石头的重量之和为sum−neg
        //则结果为:ki * stones[i]的累加和 = (sum - neg) - neg = sum - 2 * neg
        //要使最后一块石头的重量尽可能地小, neg需要在不超过 ⌊sum/2⌋ 的前提下尽可能地大
        //本问题可以看作是背包容量为 ⌊sum/2⌋,物品的重量与价值均为stones[i]的0-1背包问题。 
        int sum = Arrays.stream(stones).sum();
        int target = sum / 2;
        int[][] dp = new int[stones.length][target + 1];

        //初始化dp数组
        for(int j = stones[0]; j <= target; j++){
            dp[0][j] = stones[0];
        }

        for(int i = 1; i < stones.length; i++){
            for(int j = 1; j <= target; j++){
                if(j < stones[i]){
                    dp[i][j] = dp[i - 1][j];
                }else{
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - stones[i]] + stones[i]);
                }
            }
        }
        return sum - 2 * dp[stones.length - 1][target];
    }
}
class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = Arrays.stream(stones).sum();
        int target = sum / 2;
        int[] dp = new int[target + 1];

        for(int i = 0; i < stones.length; i++){
            for(int j = target; j >= stones[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - 2 * dp[target];
    }
}

你可能感兴趣的:(LeetCode,java)