您的位置:首页 > 其它

[算法分析与设计] leetcode 每周一题: 312. Burst Balloons

2017-12-02 12:34 399 查看
题目链接: 

312. Burst Balloons

题目大意:

给定 n 个气球, 序号为 1 到 n-1, 每个气球又对应一个分值, 分值用长度为 n 的数组 nums 表示 ; 现要求逐个打爆所有气球, 而每打爆一个序号为 i 的气球, 就可以得到 nums[left] * num[i] * nums[right] 的分值(即
coins), 其中 nums[left], nums[right] 分别表示与该气球相邻的两边的气球所对应的分值, 且该气球被打爆了之后, 就将其从序列中(气球序列 和 他们对应的分值的序列 都是)移除, 故 left, right 将会变成相邻项 ;

假设 nums[-1] = nums
= 1 ;

假设 0 <= n <= 500, 且 0 <= nums[i] <= 100 ;

例如: 给定: nums 为 [3, 1, 5, 8], 则输出应为:
167 ; ( 3*1*5 + 3*5*8 + 3*8 + 8 = 167 )

解题过程:

(1) 我一开始很单纯地觉得 每次取(除了两边端点处的气球外)分值最小的气球来打爆(若有同分/相邻/... 的情况另加判断) 的方式就可以了, 后来做了好久才发现不妥, 毕竟这样的朴素做法很容易就会浪费了极大值的价值 ;

(2) 后来(不加证明, 就随便想)想过几种方案, 发觉均有缺陷, 这点上一般可以简单地取极端情况验证 ;

(3) 然后联想到可能需要 "穷举式" 解决, 加上输入数据量确实颇小, 于是考虑动态规划, ... 然后为了验证想法, 就直接去 leetcode 上的 discussion 版块上看了下, 想法得以验证, 还顺便看到了答案 ... ( 帖子地址 )
; (

(4) 答案的思路理解起来不难..., 这种 取样 处理的模式, 可以考虑针对每次所取的点作分割, 同时要注意到这里只要将 取样点 假设为要最后一个打爆的气球的话 其两边的 子序列 间是不会互相影响的(原序列中两端的分值为 1 的气球显然是不能打爆的, 而子序列中的两端的气球与
取样点气球 的交集又只有在(假设)打爆 取样点气球 的时候), 所以子序列间打爆气球的顺序就不重要了, 同时, 显然必然会存在这么一个要最后才打爆的气球 ;

代码如下:

class Solution {
public:
int maxCoins(vector<int> & nums) {
vector<int> numbers;
numbers.push_back(1);
for (auto i : nums) {
if (i > 0) {
numbers.push_back(i);
}
}
numbers.push_back(1);

const auto n = numbers.size();
vector< vector<int> > maxCoins;
maxCoins.resize(n);
for (auto & v : maxCoins) { v.resize(n); }

for (size_t k = 2; k < n; k++) {
for (size_t left = 0; left < n - k; left++) {
auto right = left + k;
for (auto i = left + 1; i < right; i++) {
maxCoins[left][right] = max(
maxCoins[left][right],
numbers[left] * numbers[i] * numbers[right] + maxCoins[left][i] + maxCoins[i][right]
);
}
}
}
return maxCoins[0][n - 1];
}
};


Runtime: 9 ms

题目链接: 

312. Burst Balloons

题目大意:

给定 n 个气球, 序号为 1 到 n-1, 每个气球又对应一个分值, 分值用长度为 n 的数组 nums 表示 ; 现要求逐个打爆所有气球, 而每打爆一个序号为 i 的气球, 就可以得到 nums[left] * num[i] * nums[right] 的分值(即
coins), 其中 nums[left], nums[right] 分别表示与该气球相邻的两边的气球所对应的分值, 且该气球被打爆了之后, 就将其从序列中(气球序列 和 他们对应的分值的序列 都是)移除, 故 left, right 将会变成相邻项 ;

假设 nums[-1] = nums
= 1 ;

假设 0 <= n <= 500, 且 0 <= nums[i] <= 100 ;

例如: 给定: nums 为 [3, 1, 5, 8], 则输出应为:
167 ; ( 3*1*5 + 3*5*8 + 3*8 + 8 = 167 )

解题过程:

(1) 我一开始很单纯地觉得 每次取(除了两边端点处的气球外)分值最小的气球来打爆(若有同分/相邻/... 的情况另加判断) 的方式就可以了, 后来做了好久才发现不妥, 毕竟这样的朴素做法很容易就会浪费了极大值的价值 ;

(2) 后来(不加证明, 就随便想)想过几种方案, 发觉均有缺陷, 这点上一般可以简单地取极端情况验证 ;

(3) 然后联想到可能需要穷举式解决, 加上输入数据量确实颇小, 于是考虑动态规划, ... 然后为了验证想法, 就直接去 leetcode 上的 discussion 版块上看了下, 想法得以验证, 还顺便看到了答案 ... ( 帖子地址 )
;

(4) 答案的思路理解起来不难..., 这种 取样 处理的模式, 可以考虑针对每次所取的点作分割, 同时要注意到这里只要将 取样点 假设为要最后一个打爆的气球的话 其两边的 子序列 间是不会互相影响的(原序列中两端的分值为 1 的气球显然是不能打爆的, 而子序列中的两端的气球与
取样点气球 的交集又只有在(假设)打爆 取样点气球 的时候), 所以子序列间打爆气球的顺序就不重要了, 同时, 显然必然会存在这么一个要最后才打爆的气球 ;

代码如下:

class Solution {
public:
int maxCoins(vector<int> & nums) {
vector<int> numbers;
numbers.push_back(1);
for (auto i : nums) {
if (i > 0) {
numbers.push_back(i);
}
}
numbers.push_back(1);

const auto n = numbers.size();
vector< vector<int> > maxCoins;
maxCoins.resize(n);
for (auto & v : maxCoins) { v.resize(n); }

for (size_t k = 2; k < n; k++) {
for (size_t left = 0; left < n - k; left++) {
auto right = left + k;
for (auto i = left + 1; i < right; i++) {
maxCoins[left][right] = max(
maxCoins[left][right],
numbers[left] * numbers[i] * numbers[right] + maxCoins[left][i] + maxCoins[i][right]
);
}
}
}
return maxCoins[0][n - 1];
}
};


Runtime: 9 ms
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: