1671. 得到山形数组的最少删除次数
Python3
方法一: 动态规划
class Solution:
# 子模块: 获取最长递增子模块
def getLengthofLIS(self, nums):
n = len(nums)
dp = [1] * n # 这里 每个都要遍历,dp 长度确定.
# dp[i]: 以 nums[i] 结尾的 最长上升子序列 的长度
for i in range(1, n): #
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j]+1)
return dp
# 主模块
def minimumMountainRemovals(self, nums: List[int]) -> int:
# 获取 前缀 长度
pre = self.getLengthofLIS(nums)
# 获取 后缀长度
suf = self.getLengthofLIS(nums[::-1])[::-1]
length = 0
for pre_L, suf_L in zip(pre, suf):
if pre_L > 1 and suf_L > 1:
length = max(length, pre_L + suf_L - 1)
return len(nums) - length
⭐ 方法二:二分查找
这里不能 用贪心了,因为 两边分开找,总和不一定。
class Solution:
# 子模块: 获取最长递增子模块
def getLengthofLIS(self, nums):
""" 二分查找 第一次见这种写法"""
dp = [] # 这里 每个都要遍历,dp 长度确定.
# dp[i]: 以 nums[i] 结尾的 最长上升子序列 的长度
lis = []
for i, num in enumerate(nums):
it = bisect_left(lis, num) ## 在有序列表中查找将 num 插入的位置,并返回左侧的索引(相同值的最左边位置)。
if it == len(lis):
lis.append(num)
dp.append(len(lis))
else:
lis[it] = num
dp.append(it+1)
return dp
# 主模块
def minimumMountainRemovals(self, nums: List[int]) -> int:
# 获取 前缀 长度
pre = self.getLengthofLIS(nums)
# 获取 后缀长度
suf = self.getLengthofLIS(nums[::-1])[::-1]
length = 0
for pre_L, suf_L in zip(pre, suf):
if pre_L > 1 and suf_L > 1:
length = max(length, pre_L + suf_L - 1)
return len(nums) - length
C++
⭐ 方法二:二分查找
class Solution {
public:
// 主模块
int minimumMountainRemovals(vector<int>& nums) {
vector<int> pre = getLengthofLIS(nums);
vector<int> suf = getLengthofLIS({nums.rbegin(), nums.rend()}); //rbegin rend
reverse(suf.begin(), suf.end());
int length = 0;
for (int i = 0; i < nums.size(); ++i){
if (pre[i] > 1 && suf[i] > 1){
length = max(length, pre[i] + suf[i] - 1);
}
}
return nums.size() - length;
}
// 子模块
vector<int> getLengthofLIS(const vector<int> & nums){ // 要 const
vector<int> dp, lis;
for (int i = 0; i < nums.size(); ++i){
auto it = lower_bound(lis.begin(), lis.end(), nums[i]); // lower_bound
if (it == lis.end()){
lis.push_back(nums[i]);
dp.push_back(lis.size());
}
else{
*it = nums[i];
dp.push_back(it - lis.begin() + 1);
}
}
return dp;
}
};
300. 最长递增子序列
Python3
方法一: 动态规划
注意子序列的理解,不需要连续。但也不是小于就可以,还要结合 dp。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp = [] # 以 nums[i] 结尾的 最长上升子序列 的长度,这样 转移公式才不会错
for i in range(len(nums)):
dp.append(1) # 重要 提前将 dp[i] 置1
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i],dp[j]+1) # 和上一个 dp[j] 比较, 找最大的
return max(dp)
⭐ 方法二: 贪心 + 二分查找
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
d = [nums[0]] ## 记录 长度为 index + 1 的子序列的 尾数. 这个尾数越小越好
idx = 0
for i in range(1, len(nums)):
if nums[i] > d[idx]:
d.append(nums[i])
idx += 1
else: ## 更小的尾数 更新前面
left, right = 0, len(d)-1
loc = right #
while left <= right:
mid = left + (right - left)//2
if d[mid] >= nums[i]:
loc = mid ## 注意这里的写法
right = mid - 1
else:
left = mid + 1
# 更新 d[k+1]
d[loc] = nums[i]
return len(d)
C++
⭐ 方法二: 贪心 + 二分查找
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> d;
d.push_back(nums[0]);
int idx = 0;
for (int i = 1; i < nums.size(); ++i){
if (nums[i] > d[idx]){
d.push_back(nums[i]);
idx += 1;
}
else{
int left = 0, right = d.size()-1;
int loc = right;
while (left <= right){
int mid = (right + left) >> 1;
if (d[mid] >= nums[i]){
loc = mid;
right = mid - 1;
}
else{
left = mid + 1;
}
}
d[loc] = nums[i];
}
}
return d.size();
}
};