• C++二分算法:得到山形数组的最少删除次数


    题目

    我们定义 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)。

    变量解释

    vLeftLenvLeftLen[i]表示以nums[i]结尾的最长升序子序列
    vRightLenvRightLen [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

    我想对大家说的话
    闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
    子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
    如果程序是一条龙,那算法就是他的是睛
  • 相关阅读:
    Sublime Text 最简单的更换主题和字体颜色的办法
    四.镜头知识之放大倍率
    分布式电源接入对配电网影响分析(Matlab代码实现)
    【MyBatis-Plus】简介 | 入门案例
    docker拉取镜像报错Error response from daemon: Get https://registry-1.docker.io/v2/:
    如何优雅的杀掉一个进程
    CAD一键添加审图批注、AUTOCAD——图形界线怎么设置
    【数论】卡特兰数
    Overleaf论文排版踩坑记录
    运行java命令出现 Error: Invalid or corrupt jarfile XXX.jar
  • 原文地址:https://blog.csdn.net/he_zhidan/article/details/134537345