题目
我们定义 arr 是 山形数组 当且仅当它满足:
arr.length >= 3
存在某个下标 i (从 0 开始) 满足 0 < i < arr.length - 1 且:
arr[0] < arr[1] < … < arr[i - 1] < arr[i]
arr[i] > arr[i + 1] > … > arr[arr.length - 1]
给你整数数组 nums ,请你返回将 nums 变成 山形状数组 的 最少 删除次数。
示例 1:
输入:nums = [1,3,1]
输出:0
解释:数组本身就是山形数组,所以我们不需要删除任何元素。
示例 2:
输入:nums = [2,1,1,5,6,2,3,1]
输出:3
解释:一种方法是将下标为 0,1 和 5 的元素删除,剩余元素为 [1,5,6,3,1] ,是山形数组。
参数范围:
3 <= nums.length <= 1000
1 <= nums[i] <= 109
题目保证 nums 删除一些元素后一定能得到山形数组。
分析
本题可以转换成:最长山形数组,再进一步转换成最长升序子序列。
时间复杂度
O(nlogn)。两轮:一,枚举山顶及左边的长度。二,枚举山顶及右边的长度。枚举某个山顶,需要二分查询,时间复杂度O(logn)。
变量解释
vLeftLen | vLeftLen[i]表示以nums[i]结尾的最长升序子序列 |
vRightLen | vRightLen [i]表示nums[i]开头的最长降序子序列 ,转置数组后,以X开头的降序序列就变成,以XX结尾的升序序列 |
mValueLen | 键:符合条件的子系列的结尾值,值:子系列长度。 |
计算vLeftLen[i] ,计算vRightLen[i]类似
如果不存在nums[j] < nums[i] | vLeftLen[i] 为1 |
如果存在nums[j] < nums[i] | vLeftLen[i] 为1+vLeftLen[j],如果有多个j,结果取最大值 |
如果某个组合的 值大,长度小,则别淘汰。值越大,越难被选中;长度小,新长度就小。淘汰后,mValueLen|的键和值都按升序排序。
注意:
vLeftLen[i]为1或vRightLen[i]为1,无法形成山行数组。山顶左右都必须有元素。
代码
核心代码
template<class _Kty,class _Ty,bool bValueDdes,bool bOutSmallKey>
class COrderValueMap
{
public:
void Add (_Kty iValue, _Ty iNum)
{
if (bOutSmallKey)
{
if (bValueDdes)
{
AddOutSmall(iValue, iNum, std::less_equal<_Ty>(), std::greater_equal<_Ty>());
}
else
{
}
}
else
{
if (bValueDdes)
{
AddNotOutSmall(iValue, iNum, std::greater_equal<_Ty>(), std::less_equal<_Ty>());
}
else
{
AddNotOutSmall(iValue, iNum, std::less_equal<_Ty>(), std::greater_equal<_Ty>());
}
}
};
template<class _Pr1, class _Pr2>
void AddOutSmall(_Kty key, _Ty value, _Pr1 pr1, _Pr2 pr2)
{
auto it = m_map.lower_bound(key);
if ((m_map.end() != it) && pr1(it->second, value))
{
return;//被旧值淘汰
}
auto ij = it;
while (it != m_map.begin())
{
--it;
if (pr2(it->second, value))
{
it = m_map.erase(it);
}
}
m_map[key] = value;
}
template<class _Pr1, class _Pr2>
void AddNotOutSmall(_Kty key, _Ty value, _Pr1 pr1,_Pr2 pr2 )
{
auto it = m_map.upper_bound(key);
if ((m_map.begin() != it) && pr1(std::prev(it)->second, value))
{
return;//被淘汰
}
auto ij = it;
for (; (m_map.end() != ij) && pr2(ij->second, value); ++ij);
m_map.erase(it, ij);
m_map[key] = value;
};
std::map<_Kty, _Ty> m_map;
};
class Solution {
public:
int minimumMountainRemovals(vector<int>& nums) {
vector<int> vLeftLen,vRightLen;
Do(vLeftLen, nums);
Do(vRightLen, vector<int>(nums.rbegin(), nums.rend()));
std::reverse(vRightLen.begin(), vRightLen.end());
int iMaxLen = 0;
for (int i = 1; i+1 < nums.size(); i++)
{
if ((vLeftLen[i] > 1) && (vRightLen[i] > 1))
{
iMaxLen = max(iMaxLen, vLeftLen[i] + vRightLen[i] - 1);
}
}
return nums.size() - iMaxLen;
}
void Do(vector<int>& vLen, const vector<int> nums)
{
COrderValueMap<int, int, true, false> mValueLen;
for (const auto& n : nums)
{
auto it = mValueLen.m_map.lower_bound(n);
int iNewLen = 1;
if (mValueLen.m_map.begin() != it)
{
iNewLen += std::prev(it)->second;
}
mValueLen.Add(n, iNewLen);
vLen.emplace_back(iNewLen);
}
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
测试用例
template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}
template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}
int main()
{
vector nums;
int res;
{
Solution slu;
nums = { 1,3,1 };
res = slu.minimumMountainRemovals(nums);
Assert(0, res);
}
{
Solution slu;
nums = { 2, 1, 1, 5, 6, 2, 3, 1 };
res = slu.minimumMountainRemovals(nums);
Assert(3, res);
}
{
Solution slu;
nums = { 9, 8, 1, 7, 6, 5, 4, 3, 2, 1 };
res = slu.minimumMountainRemovals(nums);
Assert(2, res);
}
{
Solution slu;
nums = { 100, 92, 89, 77, 74, 66, 64, 66, 64 };
res = slu.minimumMountainRemovals(nums);
Assert(6, res);
}
{
Solution slu;
nums = { 1, 2, 1, 3, 4, 4 };
res = slu.minimumMountainRemovals(nums);
Assert(3, res);
}
//CConsole::Out(res);
- 1
}
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快
速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
---|
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
评论记录:
回复评论: