一天一大 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]
}
- c++中stack、queue、vector的用法一、栈(stack)二、队列(queue)三、向量(vector)
- React技巧7(TodoList实现3组件之间传递数据之优化)
- React技巧6(TodoList实现2组件之间传递数据)
- POJ A Knight's Journey
- React技巧5(TodoList实现)
- 求一个数n次方后的末尾数(数论/快速幂)问题描述解题思路代码实现运行结果参考
- Catch That Cow
- React技巧4(如何处理List里面的Item)
- ZOJ 3620 Escape Time II
- React技巧3(如何优雅的渲染一个List)
- FZU 电动车通行证制度
- 贪心算法总结贪心算法基本思路算法实现实例分析参考
- React技巧2(避免无意义的父节点)
- Havel定理
- 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 数组属性和方法
- 学习|Unity3d的导航实现循环线路移动
- 【redis】闲得无聊,来聊聊当下爆火的 redis集群,顺便搭一个玩玩呗
- 【redis入门】Centos下安装redis
- LeetCode精选好题(五)
- 【leetcode两题选手】MySQL类题目(一)
- 【LeetCode每日一题】(8.11)被围绕的区域
- 二叉树的前中后序遍历(迭代法)(带动画)
- 【LeetCode两题选手】算法类题目(8.8)
- 【LeetCode每日一题】(8.9)复原IP地址(回溯)
- 【回溯算法】N叉树相关技巧
- 【回溯算法】回溯,从入门到入土,七道试题精选、精讲、精练
- 数据结构练手小项目(AVL树、哈希表、循环链表、MySQL数据库)
- 【LeetCode】每日一题(8.2)二叉树展开为链表
- 【小技巧】argc和argv的用法
- 全面分析redis持久化机制