本文涉及的基础知识点
二分查找算法合集 键值皆有序
LeetCode1840. 最高建筑高度
在一座城市里,你需要建 n 栋新的建筑。这些新的建筑会从 1 到 n 编号排成一列。
这座城市对这些新建筑有一些规定:
每栋建筑的高度必须是一个非负整数。
第一栋建筑的高度 必须 是 0 。
任意两栋相邻建筑的高度差 不能超过 1 。
除此以外,某些建筑还有额外的最高高度限制。这些限制会以二维整数数组 restrictions 的形式给出,其中 restrictions[i] = [idi, maxHeighti] ,表示建筑 idi 的高度 不能超过 maxHeighti 。
题目保证每栋建筑在 restrictions 中 至多出现一次 ,同时建筑 1 不会 出现在 restrictions 中。
请你返回 最高 建筑能达到的 最高高度 。
示例 1:
输入:n = 5, restrictions = [[2,1],[4,1]]
输出:2
解释:上图中的绿色区域为每栋建筑被允许的最高高度。
我们可以使建筑高度分别为 [0,1,2,1,2] ,最高建筑的高度为 2 。
示例 2:
输入:n = 6, restrictions = []
输出:5
解释:上图中的绿色区域为每栋建筑被允许的最高高度。
我们可以使建筑高度分别为 [0,1,2,3,4,5] ,最高建筑的高度为 5 。
示例 3:
输入:n = 10, restrictions = [[5,3],[2,5],[7,4],[10,3]]
输出:5
解释:上图中的绿色区域为每栋建筑被允许的最高高度。
我们可以使建筑高度分别为 [0,1,2,3,3,4,4,5,4,3] ,最高建筑的高度为 5 。
提示:
2 <= n <= 109
0 <= restrictions.length <= min(n - 1, 105)
2 <= idi <= n
idi 是 唯一的 。
0 <= maxHeighti <= 109
二分查找
将restrictions转为哈希映射mMaxHeight
存在mMaxHeight[i1]=h1 mMaxHeight[i2]=h2,
有序映射mHeight[i]记录各建筑的最高高度,则mHeight[i1] 不超过 min(h1,h2+abs(i1-i2))。
显然:mMaxHeight小的会影响大的,大的不会影响小的。故: 从mMaxHeight[i]从小到大处理。
计算mHeight[i]时:
转移过程需要计算abs(i1-i2),所以左右要分开:
值键皆有序map mLeft,mRight
vHeight[i2]=0
处理mLeft
minSelf(&vHeight[i2],mLeft[1,i2)的最小值+(i2-1))
mLeft中的key1 < key2,能选择key2,就一定能选择key1。如果value1 < value2。则key2一定不会选择,被淘汰。
淘汰后,值降序。 mLeft[1,i2)的最小值 就是 ,小于i2的最大key。
mLeft[i2] = vHeight[i2] - (i2-1); 建筑一的等效约束。
处理mRight
minSelf(&vHeight[i2],mRight(i2,n]的最小值+(n-i2))
mRight中的key1<key2,能选择key1,就一定能选择key2。如果value2 < value1,则key1被淘汰。
淘汰后,值升序。
mRight[i2] = vHeight[i2] - (n-i2); 建筑n的等效约束。
** 注意**:
mHeight中没有的值,也要处理。
i1和i2之间的最高建筑:tmp = (i2-i1-1)-abs(vHeight[i1]-vHeight[i2]) 将i1,i2中较低的建筑填到同样高后,余下的建筑数。
max(vHeight[i1],vHeight[i2])+(tmp+1)/2
代码
核心代码
template<class _Kty, class _Ty, bool bValueAsc, bool bOutSmallKey> class COrderValueMap { public: void Add(_Kty key, _Ty value) { if (bOutSmallKey) { if (bValueAsc) { AddOutSmall(key, value, std::less_equal<_Ty>(), std::greater_equal<_Ty>()); } else { AddOutSmall(key, value, std::greater_equal<_Ty>(), std::less_equal<_Ty>()); } } else { if (bValueAsc) { AddNotOutSmall(key, value, std::greater_equal<_Ty>(), std::less_equal<_Ty>()); } else { AddNotOutSmall(key, value, std::less_equal<_Ty>(), std::greater_equal<_Ty>()); } } }; std::map<_Kty, _Ty> m_map; protected: 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); } else { break; } } 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; }; }; template<class ELE,class ELE2> void MinSelf(ELE* seft, const ELE2& other) { *seft = min(*seft,(ELE) other); } template<class ELE> void MaxSelf(ELE* seft, const ELE& other) { *seft = max(*seft, other); } class Solution { public: int maxBuilding(int n, vector<vector<int>>& restrictions) { multimap<int, int> mMaxHeightToIndex; for (const auto& v : restrictions) { mMaxHeightToIndex.emplace(v[1], v[0]); } mMaxHeightToIndex.emplace(0, 1); mMaxHeightToIndex.emplace(1000'000'000, n); COrderValueMap<int,int,false,false> mLeft; COrderValueMap<int, int, true, true> mRight; map<int, int> mHeight; int iRet = 0; for (const auto [maxHeight, index] : mMaxHeightToIndex) { mHeight[index] = maxHeight; auto it1 = mLeft.m_map.lower_bound(index); if (mLeft.m_map.begin() != it1) { MinSelf(&mHeight[index], (--it1)->second + (index - 1)); } auto it2 = mRight.m_map.lower_bound(index); if (mRight.m_map.end() != it2) { MinSelf(&mHeight[index], it2->second + (n - index )); } MaxSelf(&iRet, mHeight[index]); mLeft.Add(index, mHeight[index] - (index - 1)); mRight.Add(index, mHeight[index] - (n - index)); } for (auto it = mHeight.begin(); it != mHeight.end(); ++it ) { auto itNext = std::next(it); if (mHeight.end() == itNext) { continue; } int iMidCnt = itNext->first - it->first - 1; const int iMin = min(it->second, itNext->second); const int iMax = max(it->second, itNext->second); const int iAdd = (iMidCnt - (iMax - iMin) + 1) / 2; MaxSelf(&iRet, iMax + iAdd); } return iRet; } };
测试用例
template<class T,class T2> void Assert(const T& t1, const T2& t2) { assert(t1 == t2); } template<class T> void Assert(const vector<T>& v1, const vector<T>& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i], v2[i]); } } int main() { int n; vector<vector<int>> restrictions; { Solution sln; n = 5, restrictions = { {2,1},{4,1} }; auto res = sln.maxBuilding(n, restrictions); Assert(2, res); } { Solution sln; n = 6, restrictions = {}; auto res = sln.maxBuilding(n, restrictions); Assert(5, res); } { Solution sln; n = 10, restrictions = { {5,3},{2,5},{7,4},{10,3} }; auto res = sln.maxBuilding(n, restrictions); Assert(5, res); } }
2023年6月
class Solution { public: int maxBuilding(int n, vector<vector>& restrictions) { std::sort(restrictions.begin(), restrictions.end()); if (restrictions.empty() || (1 != restrictions.front()[0])) { restrictions.insert(restrictions.begin(), { 1, 0 }); } if (restrictions.empty() || (n != restrictions.back()[0])) { restrictions.emplace_back(vector{ n, n - 1 }); } std::map<int, int> mLeftRang; { for (const auto& v : restrictions) { if (mLeftRang.size()) { auto it = mLeftRang.rbegin(); mLeftRang[v[0]] = min(v[1], it->second + (v[0] - it->first)); } else { mLeftRang[v[0]] = v[1]; } } } std::map<int, int> mRightRang; { for (int i = restrictions.size() - 1; i >= 0; i-- ) { const auto& v = restrictions[i]; if (mRightRang.size()) { auto it = mRightRang.begin(); mRightRang[v[0]] = min(v[1], it->second + (it->first - v[0])); } else { mRightRang[v[0]] = v[1]; } } } int iRet = 0; for (auto it = mLeftRang.begin(); it != mLeftRang.end(); ++it ) { auto itNext = it; itNext++; if (itNext == mLeftRang.end()) { continue; } int iCanAdd = itNext->first - it->first ; const int iMin = min(it->second, mRightRang[itNext->first]); const int iMax = max(it->second, mRightRang[itNext->first]); const int iNeed = iMax - iMin; int iCur = (iCanAdd >= iNeed) ? (iMax + (iCanAdd - iNeed) / 2) : (iMin + iCanAdd); iRet = max(iCur, iRet); } return iRet; } };
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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
我想对大家说的话 |
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。