leetcode/76-80

目录

 

300-最长上升子序列

301-删除无效的括号

309-最佳买卖股票时机含冷静期

312-戳气球

322-零钱兑换


300-最长上升子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

 
示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

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

动态规划详解::link

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0) return 0;
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxlen = 1;
        //依次判断当前点的最长递增子序列
        for(int i=1;inums[j]){
                    //等于i之前的子序列和nums[i]构成的最长递增子序列的长度
                    dp[i] = Math.max(dp[i],dp[j]+1);
                }
            }
            maxlen = Math.max(maxlen,dp[i]);
        }
        return maxlen;
    }
}

301-删除无效的括号

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。

返回所有可能的结果。答案可以按 任意顺序 返回。

示例 1:

输入:s = "()())()"
输出:["(())()","()()()"]

示例 2:

输入:s = "(a)())()"
输出:["(a())()","(a)()()"]

link

class Solution{
    public List removeInvalidParentheses(String s) {
        List res = new ArrayList<>();
        dfs(s, res, '(', ')', 0, 0);
        return res;
    }

    private void dfs(String s, List res, char opening, char closing, int i, int lastRemoved) {
        int count = 0;
        while (i < s.length() && count >= 0) {
            if (s.charAt(i) == opening) {
                count++;
            } else if (s.charAt(i) == closing) {
                count--;
            }
            i++;
        }
        // 如果count==0 代表字符串遍历完了,open和close平衡
        // 如果count>0 代表字符串遍历完了,但有多余的open符号 此时需要从右到左,处理open符号的问题,可以理解为close符号的镜像问题。利用翻转和交换close和open符号复用逻辑
        // 如果count<0 代表字符串还没有遍历完,遇到不合法的close符号 需要从[上一个删除点, index) (注意左闭右开) 中任意删除一个close 进入dfs
        if (count < 0) { // find one illegal closing, delete it and dfs
            for (int j = lastRemoved; j < i; j++) {
                if (s.charAt(j) == closing && (j == 0 || s.charAt(j - 1) != closing)) { // prevent duplicate
                    dfs(s.substring(0, j) + s.substring(j + 1), res, opening, closing, i - 1, j);
                }
            }
        } else if (count > 0) { // more openings needs redo from right to left to remove
            dfs(new StringBuilder(s).reverse().toString(), res, closing, opening, 0, 0);
        } else { // balance, add to result
            if (opening == '(') {
                res.add(s);
            } else {
                res.add(new StringBuilder(s).reverse().toString());
            }
        }
    }
}

309-最佳买卖股票时机含冷静期

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
   卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:

输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

来源:力扣(LeetCode)
link

class Solution {
    /**
    注意到第 0 天实际上是不存在处于冷冻期的情况的,但我们仍然可以将对应的状态 f[0][1] 置为零
    f[0][2]的状态就是第0天不买入,很容易理解。而对这句话,我的想法如下:

    简单理解: 两种都是不持有,自然收益就是0了
    更具体理解:f[0][1] 其实可以存在,可以理解为当天买入卖出,收益自然也是0。
    而且会导致下一天"冷冻期”,显然是只有弊端。
    结合代码理解: f[0][1]只影响f[1][2],而f[1][2]又是由f[0][1]和f[0][2]共同决定的,
    而f[0][2]如上文所说,肯定就是0。 因此f[1][2] =Math.max(f[0][1], f[0][2]); ,
    只要f[0][1]<=0 都是不会造成影响的。
    因此我认为 f[0][1] = Integer.MIN_VALUE; 更容易理解,表示只会带来负收益
     */
public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) return 0;
        int res = 0;
        int len = prices.length;
        //dp[i][j]为对应的累积最大收益
        // dp[i][0] : 持有股票
        // dp[i][1] : 不持有股票,本日卖出,下一天为冷冻期
        // dp[i][2] : 不持有股票,本日无卖出,下一天非冷冻期(今天可以处于冷冻期,f[1][2]受到f[0][1]和f[0][2]共同决定)
        int[][] dp = new int[len][3];

        //初始状态:
        // 1: 第一天持有,只有可能是买入
        dp[0][0] = -prices[0];

        // 其实dp[0][1]、dp[0][2] 都不写,默认为0也对
        // 2. 第0天,相当于当天买入卖出,没有收益,并造成下一天冷冻期,减少选择。综合认为是负收益
        dp[0][1] = Integer.MIN_VALUE;

        // 3. 相当于没买入,收益自然为0
        dp[0][2]=0;

        for (int i = 1; i < len; i++) {
            // 持有股票: 1.昨天持有股票 2.本日买入(条件:昨天不持有,且不是卖出)
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);

            //本日卖出 : (条件:昨天持有)(今日持有今日卖出没有意义,且会导致明天冷静期,没有意义)
            dp[i][1] = dp[i - 1][0] + prices[i];

            // 不持有,但也不是卖出 : 1.昨天卖出,不持有  2.昨天没卖出,但也不持有
            dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]);
        }

        // 最后一天还持有股票是没有意义的,肯定是不持有的收益高,不用对比 dp[len-1][0]
        return Math.max(dp[len - 1][1], dp[len - 1][2]);
    }

}

312-戳气球

有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。

求所能获得硬币的最大数量。

示例 1:
输入:nums = [3,1,5,8]
输出:167
解释:
nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins =  3*1*5    +   3*5*8   +  1*3*8  + 1*8*1 = 167
示例 2:

输入:nums = [1,5]
输出:10

link

class Solution {
    public int maxCoins(int[] nums) {
        if(nums == null || nums.length == 0) {
            return 0;
        }
        int n = nums.length;
        //创建一个n+2的数组,开头和末尾都填1
        int[] arr = new int[n + 2];
        for(int i = 1; i <= n; ++i) {
            arr[i] = nums[i - 1];
        }
        arr[0]= 1;
        arr[n + 1] = 1;
        int[][] dp = new int[n + 2][n + 2];
        //从下往上遍历,i控制左边界,j控制右边界
        for(int i = n - 1; i >= 0; --i) {
            for(int j = i + 2; j <= n + 1; ++j) {
                //k在(i,j)范围内遍历气球,计算每选择一个气球的积分
                //一轮遍历完后,就能确定(i,j)的最大积分
                for(int k = i + 1; k < j; ++k) {
                    int total = arr[i] * arr[k] * arr[j];
                    total += dp[i][k] + dp[k][j];
                    dp[i][j] = Math.max(dp[i][j], total);
                }
            }
        }
        return dp[0][n + 1];
    }
}

322-零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

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

示例 1:

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

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

class Solution {
    /**
    状态:金额      
    转移:一个硬币,这个硬币的金额在coins[]数组中遍历   
    dp函数:输入金额,返回最少硬币数
     */
    public int coinChange(int[] coins, int amount) {
        //最多的硬币情况是全部是1元,共有amount个硬币,共有amount+1个状态,amount+1个金额(0,,,,account)
        int[] dp = new int[amount+1];
        //必须将所有的dp赋最大值,因为要找最小值
        Arrays.fill(dp,amount+1);
        //自底向上动态规划,金额为0,则硬币最小数也是0
        dp[0] = 0;
        for(int i=1;i<=amount;i++){
            //遍历coins的金额
            for(int coin : coins){
                //枚举金额面值大于硬币时,所需要的最少金币数
                if(i>=coin){
                    //dp[i-coin]+1 为dp[当前金额i减去当前硬币剩余的金额]加上当前硬币
                    dp[i] = Math.min(dp[i],dp[i-coin]+1);
                }
            }
        }
        return dp[amount]>amount ? -1 : dp[amount];
    }
}

 

你可能感兴趣的:(leetCode,leetcode)