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

2023-09-17 14:41:37

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

前言

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

一. 戳气球

原题链接
在这里插入图片描述

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

// 左右各加一个哨兵节点
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个气球(有两个可能是哨兵气球)

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

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

// 气球数量不足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]

那么写成代码就是:

// 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;
}

最终的完整代码:

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的初始化:

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

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

if (dfsCache[left][right] != -1) {
    return dfsCache[left][right];
}

完整代码:

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;
    }

更多推荐

全球汽车安全气囊芯片总体规模分析

安全气囊系统是一种被动安全性的保护系统,它与座椅安全带配合使用,可以为乘员提供有效的防撞保护。在汽车相撞时,汽车安全气囊可使头部受伤率减少25%,面部受伤率减少80%左右。汽车安全气囊芯片是整个系统的控制核心,并将所有外围系统的功能集于一身:数字碰撞传感器接口、展开气囊的点火回路驱动、大量的安全和诊断机制以供持续监测系

使用VSCode SSH实现公网远程连接本地服务器开发的详细教程

文章目录前言1、安装OpenSSH2、vscode配置ssh3.局域网测试连接远程服务器4.公网远程连接4.1ubuntu安装cpolar内网穿透4.2创建隧道映射4.3测试公网远程连接5.配置固定TCP端口地址5.1保留一个固定TCP端口地址5.2配置固定TCP端口地址5.3测试固定公网地址远程前言远程连接服务器工具

汽车OTA

汽车OTA(Over-The-Air)技术是指通过无线网络对汽车进行软件升级、数据传输和远程诊断等功能的技术。随着汽车行业的数字化和智能化发展,OTA技术在汽车领域的应用越来越广泛,对于提高汽车性能、降低维修成本和提升用户体验具有重要意义。一、汽车OTA技术的主要功能软件升级:通过OTA技术,汽车制造商可以为汽车提供实

Go 异常处理

代码在执行的过程中可能因为一些逻辑上的问题而出现错误functest1(a,bint)int{result:=a/breturnresult}funcmain(){resut:=test1(10,0)fmt.Println(resut)}panic:runtimeerror:integerdividebyzerogor

汽车红外夜视系统行业发展总体概况

汽车红外夜视系统是一种技术,旨在帮助驾驶员在夜间或低光条件下提供更好的视觉能力。它利用红外光谱的特性来检测和显示在正常光线下难以察觉的热能辐射。这使驾驶员能够在夜间或恶劣天气条件下更好地识别和辨别道路上的物体、行人、动物或其他车辆。汽车红外夜视系统通常包括以下主要组件:红外摄像机:这是系统的核心部件,它使用红外传感器来

简单聊聊G1垃圾回收算法整个流程 --- 理论篇 -- 下

简单聊聊G1垃圾回收算法整个流程---理论篇--下软实时性预测转移时间预测可信度GC暂停处理的调度并发标记中的暂停处理分代G1GC模式不同点新生代区域分代对象转移具体转移流程分代选择回收集合设置最大新生代区域数GC的切换GC执行的时机总结上一篇文章我们简单看了一下G1整个垃圾回收流程,但是关于G1如何计算区域回收价值和

ActiveMQ面试题(一)

文章目录前言一、什么是ActiveMQ二、ActiveMQ服务器宕机怎么办?三、丢消息怎么办四、持节化消息非常慢五、消息的不均匀消费总结前言什么是ActiveMQActiveMQ服务器宕机怎么办?丢消息怎么办持节化消息非常慢消息的不均匀消费一、什么是ActiveMQactiveMQ是一种开源的,实现了JMS1.1规范的

NPM 常用命令(八)

1、npminstall1.1命令使用npminstall[<package-spec>...]别名:add,i,in,ins,inst,insta,instal,isnt,isnta,isntal,isntall此命令安装一个包和它所依赖的任何包。如果包有一个package-lock文件,或者一个npm-shrink

数据结构与算法的力量:编写更高效的代码

文章目录为什么数据结构和算法重要?1.提高性能2.节省资源3.解决复杂问题4.改进代码质量常见数据结构和算法数据结构1.数组(Array)2.链表(LinkedList)3.栈(Stack)4.队列(Queue)算法1.排序算法2.搜索算法3.递归算法编写高效的代码的关键考虑因素1.时间复杂度2.空间复杂度3.数据的组

Android 自定义加解密播放音视频(m3u8独立加密)

文章目录背景加密流程音视频解密音视频播放结语背景当涉及App内部视频的时候,我们不希望被别人以抓包的形式来爬取我们的视频大视频文件以文件方式整个加密的话需要完全下载后才能进行解密当前m3u8格式虽然支持加密,但是ts格式的小视频可以独立播放的,也就是ts文件本身没有被加密,或者加密方法过于复杂根据以上,我通过修改Exo

Linux 多线程( 进程VS线程 | 线程控制 )

文章目录Linux进程VS线程进程的多个线程共享进程和线程的关系线程创建pthread_create获取线程IDpthread_self线程等待pthread_join终止线程进程分离线程ID及进程地址空间布局Linux进程VS线程进程是资源分配的基本单位。线程是OS调度的基本单位。线程共享进程数据,但也拥有自己的一部

热文推荐