LeetCode刷题——分治问题

分治问题

分治问题由“分”(divide)和“治”(conquer)两部分组成,通过把原问题分为子问题,再将子问题进行处理合并,从而实现对原问题的求解。
另外,自上而下的分治可以和memoization 结合,避免重复遍历相同的子问题。如果方便推导,也可以换用自下而上的动态规划方法求解。

241. DifferentWays to Add Parentheses (Medium)

问题说明
给你一个由数字和运算符组成的字符串 expression ,按不同优先级组合数字和运算符,计算并返回所有可能组合的结果。你可以按任意顺序 返回答案。

输入输出样例
示例 1:

输入:expression = “2-1-1”
输出:[0,2]
解释:
((2-1)-1) = 0
(2-(1-1)) = 2

示例 2:

输入:expression = "2*3-4*5"
输出:[-34,-14,-10,-10,10]
解释:

(2*(3-(4*5))) = -34 
((2*3)-(4*5)) = -14 
((2*(3-4))*5) = -10 
(2*((3-4)*5)) = -10 
(((2*3)-4)*5) = 10

思路
对于一个表达式,可以将其按照符号分成两个部分。对两个部分分别求解值,之后再将两部分求解的值进行加减乘的操作。注意边界条件:如果一个表达式中没有符号,则直接将其加到结果中。

代码

class Solution {
    public List<Integer> diffWaysToCompute(String expression) {
        LinkedList<Integer> result = new LinkedList<>();
        for(int i=0;i<expression.length();i++){
            char c = expression.charAt(i);
            if(c=='+'||c=='-'||c=='*'){
                List<Integer> left_result = diffWaysToCompute(expression.substring(0,i)); //[0,i)
                List<Integer> right_result = diffWaysToCompute(expression.substring(i+1));//[i+1,length)
                for(int left:left_result){
                    for(int right:right_result){
                        if(c == '+')result.add(left+right);
                        if(c == '*')result.add(left*right);
                        if(c == '-')result.add(left-right);
                    }
                }
            }
            
        }
        //如果expression中只有数字
        if(result.isEmpty())result.add(Integer.valueOf(expression));
        return result;
    }
}

932. Beautiful Array (Medium)

问题描述
对于某些固定的 N,如果数组 A 是整数 1, 2, …, N 组成的排列,使得:
对于每个 i < j,都不存在 k 满足 i < k < j 使得 A[k] * 2 = A[i] + A[j]。
那么数组 A 是漂亮数组。
给定 N,返回任意漂亮数组 A(保证存在一个)。

输入输出样例
示例 1:

输入:4
输出:[2,1,4,3]

示例 2:

输入:5
输出:[3,1,2,5,4]

思路
根据题意只要不满足A[k] * 2 = A[i] + A[j]即为漂亮数组,也就是说对于【1-n】可能存在很多种。这里我们只要找到每个n的其中一种即可。要不满足上述等式,只需要A[i]和A[j]一个为偶数一个为奇数即可。

对于n个数,从1开始,奇数有(n+1)/2个,偶数有n/2个
(n+1)/2个数组成的漂亮数组为left
n/2个数组成的漂亮数组为right
left*2-1依旧是漂亮数组(包括1 3 5 7…)奇漂亮数组
right*2也依旧是漂亮数组(包括2 4 6 8…)偶漂亮数组
left*2-1right*2左右拼在一起(奇漂亮数组与偶漂亮数组合并是漂亮数组),正好包含了n个数,且是漂亮数组。

代码

class Solution {
    public int[] beautifulArray(int n) {
        if(n==1) return new int[]{1};
        int[] left = beautifulArray((n+1)/2);
        int[] right = beautifulArray(n/2);
        int[] result = new int[n];
        int k = 0;
        for(int c:left){
            result[k++]=c*2-1;
        }
        for(int c:right){
            result[k++]=c*2;
        }
        return result;
    }
}

312. Burst Balloons (Hard)

问题描述
有 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

思路
借鉴LeetCode@狗大王
这个区间的气球长这样:
在这里插入图片描述
假设这个区间是个开区间,最左边索引 i,最右边索引 j,这里说 “开区间” 的意思是只能戳爆 i 和 j 之间的气球。
DP思路是这样的,就先别管前面是怎么戳的,只要管这个区间最后一个被戳破的是哪个气球,这最后一个被戳爆的气球就是 k。假设最后一个被戳爆的气球是粉色的,k 就是粉色气球的索引:
在这里插入图片描述
由于是最后一个被戳破,开区间首尾只剩 i 和 j 了。所以DP的状态转移方程是只和 i 和 j 位置的数字有关。
假设 dp[i][j] 表示开区间 (i,j) 内你能拿到的最多金币,在 (i,j) 开区间得到的金币可以由dp[i][k]dp[k][j]进行转移,此时戳破k得到的金币数量为:
dp[i][k]+val[i] * val[k] * val[j]+dp[k][j]
(i,j) 开区间可以选的 k 是有多个的,从可选的k中选择最大的值来更新dp[i][j]

代码

class Solution {
    public int maxCoins(int[] nums) {
        int n = nums.length;
        // 创建一个辅助数组,并在首尾各添加1,方便处理边界情况
        int[] temp = new int[n+2];
        temp[0] = 1;
        temp[n+1] = 1;
        for(int i=0; i<n; i++){
            temp[i+1] = nums[i];
        }
        int m = n+2; //表示扩充后数组长度
        int[][] dp = new int[m][m];
        // len表示开区间长度
        for(int len=2; len<m; len++){ //遍历每一个区间长度
            // i表示开区间左端点
            for(int i=0; i<m-len; i++){ //区间向右滑动
                range_best(i,i+len,temp,dp); //在当前区间中找到获得最多金币
            }
        }
        return dp[0][m-1];
    }

	//在(i,j)开区间中获得的最多的金币
    private void range_best(int i,int j,int[]nums,int[][]dp){
        int max = 0;
        for(int k=i+1;k<j;k++){ //k是区间(i,j)开区间内
            int left = dp[i][k];
            int right = dp[k][j];
            max = Math.max(max,left+right+nums[i]*nums[k]*nums[j]);
        }
        dp[i][j]= max;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值