312. 戳气球

题目

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

现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1i + 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

提示:

  • n == nums.length
  • 1 <= n <= 300
  • 0 <= nums[i] <= 100

代码

完整代码

#include <stdio.h>
#include <stdlib.h>

#define MAX(a,b) ((a) > (b) ? (a) : (b))

typedef struct {
    int val;
    int oldindex;
} st_t;

int cmp(const void *a, const void *b) {
    return (*(st_t*)b).val - (*(st_t*)a).val;
}

int calcscore(int* arr, int thisInputIndex, int thisInputVal, int numsSize) {
    int beforevalue = 1;
    int aftervalue = 1;
    
    // Find the first non -1 value on the right
    for (int i = thisInputIndex + 1; i < numsSize; i++) {
        if (arr[i] != -1) {
            aftervalue = arr[i];
            break;
        }
    }
    
    // Find the first non -1 value on the left
    for (int i = thisInputIndex - 1; i >= 0; i--) {
        if (arr[i] != -1) {
            beforevalue = arr[i];
            break;
        }
    }

    arr[thisInputIndex] = thisInputVal;
    return thisInputVal * beforevalue * aftervalue;
}

void dfs(int* arr, int* nums, int numsSize, int* score, int* highestScore) {
    int found = 0;
    for (int i = 0; i < numsSize; i++) {
        if (arr[i] == -1) {
            found = 1;
            int nowscore = calcscore(arr, i, nums[i], numsSize);
            *score += nowscore;
            dfs(arr, nums, numsSize, score, highestScore);
            *score -= nowscore;
            arr[i] = -1; // Reset the position after recursion
        }
    }

    if (!found && *score > *highestScore) {
        *highestScore = *score;
    }
}

int maxCoins(int* nums, int numsSize) {
    int score = 0;
    int highestScore = 0;
    
    int* arr = (int*)malloc(numsSize * sizeof(int));
    for (int i = 0; i < numsSize; i++) {
        arr[i] = -1; // 初始化为-1,表示该位置还没有填入数字
    }
    
    dfs(arr, nums, numsSize, &score, &highestScore);
    
    free(arr);
    
    return highestScore;
}

思路分析

  1. 回溯算法:通过深度优先搜索(DFS)来枚举所有可能的戳气球顺序,计算每种顺序下能获得的最大硬币数量。
  2. 计算分数:定义 calcscore 函数来计算戳破当前气球时可以获得的硬币数,考虑到边界条件处理。
  3. 记录最高分:在 DFS 过程中记录获得的最高硬币数。

拆解分析

  • 回溯搜索:遍历数组,每次选取一个未被戳破的气球进行递归处理。
  • 分数计算:计算每次戳破气球后的硬币数,并在递归返回时进行回溯。
  • 结束条件:当所有气球都被戳破时,更新最高硬币数。

复杂度分析

  • 时间复杂度O(n!),其中 n 是数组 nums 的长度。每次递归都要对剩余未戳破的气球进行选择,因此是一个阶乘级别的复杂度。
  • 空间复杂度O(n),递归栈的深度最大为数组 nums 的长度。

结果

在这里插入图片描述

一题多解

动态规划

动态规划思路分析

  1. 定义状态:使用二维数组 dp,其中 dp[i][j] 表示戳破 nums[i...j] 区间内所有气球可以获得的最大硬币数量。
  2. 状态转移:枚举区间内所有可能的最后一个被戳破的气球 k,计算戳破 k 号气球后的分数贡献,并加上 k 两侧已戳破气球的最大分数贡献。
  3. 初始化:所有单个气球戳破后获得的硬币数是 nums[i-1] * nums[i] * nums[i+1]
  4. 结果记录dp[0][n-1] 即为所求的最大硬币数。

动态规划拆解分析

最重要的就是这个三重循环:

    for (int length = 2; length < n; length++) {
        for (int left = 0, right = left + length; left < n - length; left++) {
            for (int i = left + 1; i < right; i++) {
                dp[left][right] = MAX(dp[left][right], newNums[left] * newNums[i] * newNums[right] + dp[left][i] + dp[i][right]);
            }
        }
    }
  • 第一层:for (int length = 2; length < n; length++)
    作用为控制区间长度,因为最终我们需要获得从0 ~ numsSize这个区间的最大值,因此我们需要依次获取长度为 numsSize, numsSize -1, ……, 2,之所以从2开始是因为如果len = 1,不用算,score就是nums[i];
  • 第二层for (int left = 0, right = left + length; left < n - length; left++)
    控制左右边界,从0开始依次扫描一遍各个可能的区间,计算每个可能的区间的得分。
  • 第三层for (int i = left + 1; i < right; i++)
    在区间内戳破每个气球,看看这个区间内戳破哪个气球使得总得分最高,其中,dp[left][i] dp[i][right]是戳破从左到i和从i到右的所有气球的分数,这样就保证了第三层循环内戳破第i个气球时,所有其他气球都被戳破并积分。

动态规划复杂度分析

  • 时间复杂度O(n^3),三重循环枚举所有可能的区间和最后一个戳破的气球。
  • 空间复杂度O(n^2),使用二维数组存储 dp 值。

动态规划代码

#include <stdio.h>
#include <stdlib.h>
#define MAX(a,b) ((a) > (b) ? (a) : (b))

int maxCoins(int* nums, int numsSize) {
    int n = numsSize + 2;
    int* newNums = (int*)malloc(n * sizeof(int));
    newNums[0] = newNums[n - 1] = 1;
    for (int i = 0; i < numsSize; i++) {
        newNums[i + 1] = nums[i];
    }
    
    int** dp = (int**)malloc(n * sizeof(int*));
    for (int i = 0; i < n; i++) {
        dp[i] = (int*)calloc(n, sizeof(int));
    }
    
    for (int length = 2; length < n; length++) {
        for (int left = 0, right = left + length; left < n - length; left++) {
            for (int i = left + 1; i < right; i++) {
                dp[left][right] = MAX(dp[left][right], newNums[left] * newNums[i] * newNums[right] + dp[left][i] + dp[i][right]);
            }
        }
    }
    
    int result = dp[0][n - 1];
    
    for (int i = 0; i < n; i++) {
        free(dp[i]);
    }
    free(dp);
    free(newNums);
    
    return result;
}

结果

在这里插入图片描述

总结

dp相比于dfs暴力枚举所有情况,会大量使用之前得到的值来起到剪枝的效果,对于现在大家不缺空间来说肯定是dp更优,但是如果用在小型嵌入式系统,ram不够的话,dfs+剪枝也是一种可以考虑的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/693963.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch 实现目标检测一(Pytorch 23)

一 目标检测和边界框 在图像分类任务中&#xff0c;我们假设图像中只有一个主要物体对象&#xff0c;我们只关注如何识别其类别。然而&#xff0c;很多时候图像里有多个我们感兴趣的目标&#xff0c;我们不仅想知 道它们的类别&#xff0c;还想得到它们在图像中的具体位置。在…

【Python】数据处理:OS目录文件操作

Python的os模块是一个用于与操作系统进行交互的标准库模块。它提供了丰富的功能来处理文件和目录、执行系统命令、获取和设置环境变量等。 工作目录操作 获取当前工作目录 os.getcwd()参数&#xff1a;无返回值&#xff1a;一个字符串&#xff0c;表示当前工作目录的路径。这…

算数运算符与表达式(打印被10整除的数)

打印100以内&#xff08;包含100&#xff09;能被10整除的正整数 #include <stdio.h>#define UPPER 100int main() {int i 1;while (i < UPPER)if (i % 10 0)printf("%d\n", i);return 0; } 自增运算符 i 用于递增变量 i 的值。在 while 循环中&#xf…

Word多级标题编号不连续、一级标题用大写数字二级以下用阿拉伯数字

Word多级标题编号不连续 &#xff1a; 一级标题用大写数字二级以下用阿拉伯数字&#xff1a;

Golang——gRPC与ProtoBuf介绍

一. 安装 1.1 gRPC简介 gRPC由google开发&#xff0c;是一款语言中立&#xff0c;平台中立&#xff0c;开源的远程过程调用系统。gRPC客户端和服务器可以在多种环境中运行和交互&#xff0c;例如用java写一个服务器端&#xff0c;可以用go语言写客户端调用。 1.2 gRPC与Protob…

Gitte的使用(Windows/Linux)

Gitte的使用&#xff08;Windows/Linux&#xff09; 一、Windows上使用Gitte1.下载程序2.在Gitte上创建远程仓库3.连接远程仓库4.推送文件到远程仓库 二、Linux上使用Gitte1.第一次从仓库上传1.1生成公钥1.2配置SSH公钥1.3新建一个仓库1.4配置用户名和邮箱在Linux中1.5创建仓库…

在vscode 中使用npm的问题

当我装了 npm和nodejs后 跑项目在 文件中cmd的话可以直接运行但是在 vscode 中运行的时候就会报一下错误 解决方法就是在 vscode 中吧 power shell换成cmd 来运行就行了

Java——简单图书管理系统

前言&#xff1a; 一、图书管理系统是什么样的&#xff1f;二、准备工作分析有哪些对象&#xff1f;画UML图 三、实现三大模块用户模块书架模块管理操作模块管理员操作有这些普通用户操作有这些 四、Test测试类五、拓展 哈喽&#xff0c;大家好&#xff0c;我是无敌小恐龙。 写…

C++输入输出与IO流

C 输入输出与I/O流 文章目录 C 输入输出与I/O流IO类型与基础特性概念与特性IO状态输出缓冲区 文件输入输出文件模式 string流IO处理中常用的函数及操作符综合练习与demo一、 创建文件并写入二、控制台输入数据并拆分存储三、读写电话簿 IO类型与基础特性 C11标准提供了几种IO处…

string经典题目(C++)

文章目录 前言一、最长回文子串1.题目解析2.算法原理3.代码编写 二、字符串相乘1.题目解析2.算法原理3.代码编写 总结 前言 一、最长回文子串 1.题目解析 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 示例 1&#xff1a; 输入&#xff1a;s “babad” 输出&am…

Spring @Transactional 事务注解

一、spring 事务注解 1、实现层(方法上加) import org.springframework.transaction.annotation.Transactional;Transactional(rollbackFor Exception.class)public JsonResult getRtransactional() {// 手动标记事务回滚TransactionAspectSupport.currentTransactionStatus…

# 梯影传媒T6投影仪刷机方法及一些刷机工具链接

梯影传媒T6投影仪刷机方法及一些刷机工具链接 文章目录 梯影传媒T6投影仪刷机方法及一些刷机工具链接1、安装驱动程序2、备份设备rom【boot、system】3、还原我要刷进设备的rom【system】4、打开开发者模式以便于安装apk5、root设备6、更多好链接&#xff1a; 梯影传媒T6使用的…

【嵌入式】波特率9600,发送8个字节需要多少时间,如何计算?

问题&#xff1a; 波特率9600&#xff0c;发送 01 03 00 00 00 04 44 09 (8字节) 需要多少时间&#xff0c;如何计算&#xff1f; 在计算发送数据的时间时&#xff0c;首先要考虑波特率以及每个字符的数据格式。对于波特率9600和标准的UART数据格式&#xff08;1个起始位&…

预期值与实际值对比

编辑实际值和预期值变量 因为在单独的代码当中&#xff0c;我们先定义了变量str&#xff0c;所以在matcher时传入str参数&#xff0c;但当我们要把这串代码写在testrun当中&#xff0c;改下传入的参数&#xff0c;与excel表做连接 匹配的结果是excel表中的expect结果&#xf…

质量小议38 -- 60岁退休的由来

总是要有个标准&#xff0c;质量更是如些。 标准不是固定不变的&#xff0c;与时俱进。 关键词&#xff1a;当时的人均寿命&#xff1b;渐进式 60岁退休。 22大学毕业开始工作&#xff08;当然可能会更早&#xff09;&#xff0c;到60岁退休&#xff0c;要工作38年。 …

从零入手人工智能(2)——搭建开发环境

1.前言 作为一名单片机工程师&#xff0c;想要转型到人工智能开发领域的道路确实充满了挑战与未知。记得当我刚开始这段旅程时&#xff0c;心中充满了迷茫和困惑。面对全新的领域&#xff0c;我既不清楚如何入手&#xff0c;也不知道能用人工智能干什么。正是这些迷茫和困惑&a…

SpringBoot+Vue体育馆管理系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 学生管理员 功能截图

(四)React组件、useState

1. 组件 1.1 组件是什么 概念&#xff1a;一个组件就是用户界面的一部分&#xff0c;它可以有自己的逻辑和外观&#xff0c;组件之间可以相互嵌套&#xff0c;也可以复用多次。 组件化开发可以让开发者像搭积木一样构建一个完整的庞大应用 1.2 React组件 在React中&#xf…

java中集合List,Set,Queue,Map

Java SE中的集合框架是一组用于存储和操作对象的类和接口。它提供了丰富的数据结构&#xff0c;可以用于解决各种问题。Java SE中的集合框架包含以下主要类和接口&#xff1a; 一. Collection接口&#xff1a; 是集合框架的根接口&#xff0c;它定义了一些通用的集合操作方法…

kafka-生产者事务-数据传递语义事务介绍事务消息发送(SpringBoot整合Kafka)

文章目录 1、kafka数据传递语义2、kafka生产者事务3、事务消息发送3.1、application.yml配置3.2、创建生产者监听器3.3、创建生产者拦截器3.4、发送消息测试3.5、使用Java代码创建主题分区副本3.6、屏蔽 kafka debug 日志 logback.xml3.7、引入spring-kafka依赖3.8、控制台日志…