想要精通算法和SQL的成长之路 - 戳气球

想要精通算法和SQL的成长之路 - 戳气球

  • 前言
  • [一. 戳气球](#一. 戳气球)
    • [1.1 记忆化搜索](#1.1 记忆化搜索)

前言

想要精通算法和SQL的成长之路 - 系列导航

一. 戳气球

原题链接

首先我们看一下题干:对于超出了数组边界的,就当做它是一个数字为1的气球。遇到这种的,我们可以考虑给数组边界添加哨兵。其值为1。

java 复制代码
// 左右各加一个哨兵节点
public int maxCoins(int[] nums) {
    // 1.先处理特殊情况
    if (nums.length == 1) {
        return nums[0];
    }
    int len = nums.length;
    // 左右各加一个哨兵节点
    int[] arr = new int[len + 2];
    arr[0] = 1;
    arr[len + 1] = 1;
    for (int i = 1; i < len + 1; i++) {
        arr[i] = nums[i - 1];
    }
    return ???
}

其次,我们假设有这么一个函数dfs,代表在(left,right)开区间内,可以获得的最大硬币数量。为啥是开区间?因为我们为数组添加了两个哨兵,这俩哨兵不应该在处理范围内。同时,设置俩哨兵的意义也就是:我们认定,每次戳气球的时候,必定存在至少3个气球(有两个可能是哨兵气球)

java 复制代码
// nums 在(left,right)开区间内戳气球
public int dfs(int[] nums, int left, int right) {
}

那么,dfs递归,我们首先要写的就是他的递归终止条件:

java 复制代码
// 气球数量不足3个
if (right - left < 2) {
    return 0;
}
// 气球数量正好3个
if (right - left == 2) {
	// 我们只能戳破中间的气球(左右两侧是哨兵),获得对应硬币数量
    return nums[left] * nums[left + 1] * nums[right];
}

其次,dfs递归,我们做啥事情?根据前面的递归终止条件可以判断出,此时气球的数量必定 > 3个。我们假设戳气球的步骤:

  1. 先把第k个气球的左侧给戳破完,那么左侧区域能获得的最大硬币数量为:dfs(nums, left,k)
  2. 再把第k个气球的右侧给戳破完,那么右侧区域能获得的最大硬币数量为:dfs(nums, k,right)
  3. 最后戳破第k个气球,那么戳破当前气球获得的硬币数量为:nums[k] * nums[left] * nums[right]

那么写成代码就是:

java 复制代码
// nums 在(left,right)开区间内戳气球
public int dfs(int[] nums, int left, int right) {
    int res = 0;
    // 气球数量不足3个
    if (right - left < 2) {
        return 0;
    }
    // 气球数量正好3个
    if (right - left == 2) {
        return nums[left] * nums[left + 1] * nums[right];
    }
    // 在[left+1,right-1]闭区间内遍历,选取每次遍历的当前节点作为 最后戳破 的气球(最后戳破。最后戳破。最后戳破)
    for (int k = left + 1; k < right; k++) {
        // 戳破第k个气球,该气球左侧的最大硬币数
        int leftCount = dfs(nums, left, k);
        int rightCount = dfs(nums, k, right);
        int currentFinalCount = nums[k] * nums[left] * nums[right];
        res = Math.max(res, leftCount + rightCount + currentFinalCount);
    }
    return res;
}

最终的完整代码:

java 复制代码
public int maxCoins(int[] nums) {
    // 1.先处理特殊情况
    if (nums.length == 1) {
        return nums[0];
    }
    int len = nums.length;
    // 左右各加一个哨兵节点
    int[] arr = new int[len + 2];
    arr[0] = 1;
    arr[len + 1] = 1;
    for (int i = 1; i < len + 1; i++) {
        arr[i] = nums[i - 1];
    }
    return dfs(arr, 0, len + 1);
}

// nums 在(left,right)开区间内戳气球
public int dfs(int[] nums, int left, int right) {
    int res = 0;
    // 气球数量不足3个
    if (right - left < 2) {
        return 0;
    }
    // 气球数量正好3个
    if (right - left == 2) {
        return nums[left] * nums[left + 1] * nums[right];
    }
    // 在[left+1,right-1]闭区间内遍历,选取每次遍历的当前节点作为 最后戳破 的气球(最后戳破。最后戳破。最后戳破)
    for (int k = left + 1; k < right; k++) {
        // 戳破第k个气球,该气球左侧的最大硬币数
        int leftCount = dfs(nums, left, k);
        int rightCount = dfs(nums, k, right);
        int currentFinalCount = nums[k] * nums[left] * nums[right];
        res = Math.max(res, leftCount + rightCount + currentFinalCount);
    }
    return res;
}

1.1 记忆化搜索

填充书架 一样,我们在dfs递归的时候,有大量的重复计算,我们用一个全局的dfsCache做一下缓存即可。

dfsCache的初始化:

java 复制代码
dfsCache = new int[len + 2][len + 2];
for (int i = 0; i < len + 2; i++) {
    Arrays.fill(dfsCache[i], -1);
}

dfsCache的作用体现:如果发现计算过这个值,直接返回,后续的递归直接不用做了。

java 复制代码
if (dfsCache[left][right] != -1) {
    return dfsCache[left][right];
}

完整代码:

java 复制代码
public class Test312 {
    int[][] dfsCache;

    public int maxCoins(int[] nums) {
        // 1.先处理特殊情况
        if (nums.length == 1) {
            return nums[0];
        }
        int len = nums.length;
        // 左右各加一个哨兵节点
        int[] arr = new int[len + 2];
        arr[0] = 1;
        arr[len + 1] = 1;
        for (int i = 1; i < len + 1; i++) {
            arr[i] = nums[i - 1];
        }
        dfsCache = new int[len + 2][len + 2];
        for (int i = 0; i < len + 2; i++) {
            Arrays.fill(dfsCache[i], -1);
        }
        return dfs(arr, 0, len + 1);
    }

    // nums 在(left,right)开区间内戳气球
    public int dfs(int[] nums, int left, int right) {
        int res = 0;
        // 气球数量不足3个
        if (right - left < 2) {
            return 0;
        }
        // 气球数量正好3个
        if (right - left == 2) {
            return nums[left] * nums[left + 1] * nums[right];
        }
        if (dfsCache[left][right] != -1) {
            return dfsCache[left][right];
        }
        // 在[left+1,right-1]闭区间内遍历,选取每次遍历的当前节点作为 最后戳破 的气球(最后戳破。最后戳破。最后戳破)
        for (int k = left + 1; k < right; k++) {
            // 戳破第k个气球,该气球左侧的最大硬币数
            int leftCount = dfs(nums, left, k);
            int rightCount = dfs(nums, k, right);
            int currentFinalCount = nums[k] * nums[left] * nums[right];
            res = Math.max(res, leftCount + rightCount + currentFinalCount);
        }
        return dfsCache[left][right] = res;
    }
相关推荐
m0_613856294 小时前
mysql如何利用事务隔离级别解决特定业务冲突_mysql隔离方案选型
jvm·数据库·python
Adios7944 小时前
VPR:Pitts50K和Norland数据集下载
数据库
东风破1374 小时前
DM用户权限、表、约束等对象的基本操作,SQL日志的开启介绍
数据库·sql·dm达梦数据库
收获不止数据库4 小时前
达梦9发布会归来:AI 时代,我们需要一款什么样的数据库?
数据库·人工智能·ai·语言模型·数据分析
昵称小白4 小时前
复杂度分析方法
算法
小宇的天下4 小时前
Virtuoso GUI 界面中的关键模块定义
数据库
bqq198610265 小时前
MySQL 5.7 与 MySQL 8.0 的主要区别
数据库·mysql
juniperhan5 小时前
Flink 系列第21篇:Flink SQL 函数与 UDF 全解读:类型推导、开发要点与 Module 扩展
java·大数据·数据仓库·分布式·sql·flink
科研前沿5 小时前
2026 数字孪生前沿科技:全景迭代报告 —— 镜像视界生成式孪生(Generative DT)技术白皮书
大数据·人工智能·科技·算法·音视频·空间计算
Elastic 中国社区官方博客5 小时前
Elastic-caveman : 在不损失 Elastic 最佳效果的情况下,将 AI 响应 tokens 减少64%
大数据·运维·数据库·人工智能·elasticsearch·搜索引擎·全文检索