一天一大 leet(戳气球)难度:困难-Day20200719
时间:2022-07-25
本文章向大家介绍一天一大 leet(戳气球)难度:困难-Day20200719,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
题目:
有 n 个气球,编号为 0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。如果你戳破气球 i ,就可以获得 nums[left] x nums[i] x nums[right] 个硬币。这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。
求所能获得硬币的最大数量。
说明:
- 你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破
- 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
示例:
输入: [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
抛砖引玉
思路
- 枚举所有可能求最大值
- 首先起点不一定,而且起点之后路径也不确定
- 起点可以枚举,路径就不太好枚举了
- 那么尝试使用递归,每次传入处理后的数组再次枚举所有起点,那么就枚举了所有可能
实现
/**
* @param {number[]} nums
* @return {number}
*/
var maxCoins = function (nums) {
let _result = 0
function maxItem(list, result) {
if (list.length === 0) {
_result = Math.max(_result, result)
return
}
for (let i = 0; i < list.length; i++) {
let item = (list[i - 1] || 1) * list[i] * (list[i + 1] || 1)
maxItem(
list.filter((val, index) => index !== i),
result + item
)
}
}
maxItem(nums, _result)
return _result
}
每一个起点都计算了所有可能的组合,超时也在意料之中。想到优化,优先想到的是能不能存储下已经出现过的组合,发现 i 变换的过程中左右的元素也在变化似乎没有维度可以存储。
记忆化搜索
那换个思路,不存储点,存储范围试一下:
- 设起始点 i,结束点 j
- dp[i][j]表示戳破 i 到 j 之间气球能得到的最大积分
- dp 的范围:0 和 n 都参与运算则 dp(n+2)(n+2)
怎么得到 dp[i][j]的值?
- 如果 nums 长 3 就很简单了:dp[0][2] = 0 + nums[0]*nums[1]*nums[2]* + 0 其中第一个 0 也可以用 dp[0][1]表示,第二个 0 也可以用 dp[0][3]表示 即: dp[0][2] = dp[0][1] + nums[0]*nums[1]*nums[2]* + dp[0][3]
- 当 i 到 j 中存在大于 1 个元素时 dp[i][j]就会存在多种结果,需要枚举所有结果去最大值 设枚举 i 到 j 范围的指针为 k(k 大于 i 且 k 小于 j),则: dp[i][j] = dp[i][k] + nums[i] * nums[k] * nums[j] + dp[k][j]
实现
/**
* @param {number[]} nums
* @return {number}
*/
var maxCoins = function (nums) {
// 填充收尾默认的1,在添加后取length生成dp
nums.unshift(1)
nums.push(1)
let len = nums.length,
dp = Array.from({ length: len }, () => new Array(len).fill(-1))
// 去除添加的两个1,i和j的范围即要求的值
return solve(0, len - 1)
function solve(i, j) {
// 超出范围 返回0
if (i >= j - 1) return 0
// 该范围已经计算过
if (dp[i][j] != -1) return dp[i][j]
for (let k = i + 1; k < j; k++) {
let sum = nums[i] * nums[k] * nums[j]
sum += solve(i, k) + solve(k, j)
dp[i][j] = Math.max(dp[i][j], sum)
}
return dp[i][j]
}
return dp[0][len - 1]
}
动态规划
- 从 i 和 j 的边界开始枚举
- 另外因为 dp[i][j]依赖 dp[i][k]、dp[k][j]
- 即在计算 dp[i][j]时 dp[i][k]、dp[k][j],需要已知,那用实例看下,i 为 0,j 为 5,设 求 dp[1][3]k 为 2 我们需要知道 dp[1][2]、dp[2][3] (0,标识未知,1 表示 1 知)
# |
3 |
1 |
5 |
8 |
---|---|---|---|---|
3 |
null |
dp[0][1]->0 |
dp[0][2]->0 |
dp[0][3]->0 |
1 |
null |
null |
dp[1][2]->1 |
dp[1][3]->0 |
5 |
null |
null |
null |
dp[2][3]->1 |
8 |
null |
null |
null |
null |
会发现,i 需要从大到小变量
/**
* @param {number[]} nums
* @return {number}
*/
var maxCoins = function (nums) {
nums.unshift(1)
nums.push(1)
let len = nums.length,
dp = Array.from({ length: len }, () => new Array(len).fill(0))
// 默认填充的1不能被戳破,则i的边界为len-2-1
// (i<j,则i最大为len-1,去除默认则未len-2-1)
for (let i = len - 3; i >= 0; i--) {
// i<j,则j最小为i,去除默认则未i+1
for (let j = i + 2; j < len; j++) {
for (let k = i + 1; k < j; k++) {
dp[i][j] = Math.max(
dp[i][j],
dp[i][k] + nums[i] * nums[k] * nums[j] + dp[k][j]
)
}
}
}
return dp[0][len - 1]
}
- 10.20 firewalld的9个zone
- 英语不好,数学也不好,能不能学WEB前端?
- 10.19 iptables规则备份和恢复
- 11.6 MariaDB安装
- cocos2dx-v3.4 2048(四):游戏逻辑的设计与实现
- Linux基础(day39)
- Chrome扩展程序之编码&时间戳小工具
- WINDOWS下烧一只鹅
- 11.3/11.4/11.5 MySQL安装
- Greenrobot-EventBus源码学习(六)
- Greenrobot-EventBus源码学习(五)
- writeup分享 | 近期做的比较好的web
- Greenrobot-EventBus源码学习(四)
- Linux基础(day38)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 聊聊AbstractProcessor和Java编译流程
- Okhttp如何开启的Http2.0
- PHP Execute Command Bypass Disable_functions
- 聊聊Android编译流程
- Android组件化问题思考
- 最近面试碰到的两道算法题|面试相关
- Thread也会OOM吗?
- RoundCube Webmail邮件正文存储型XSS(CVE-2015-1433)
- 再谈Android Lint
- Android DiffUtil 封装|深拷贝
- [CVE-2014-8959] phpmyadmin任意文件包含漏洞分析
- Android 统计页面渲染时长
- Transform和Task之间有关?| Gradle
- user.ini文件构成的PHP后门
- Android厂商推送Plugin化 | 掘金技术征文-双节特别篇