【二分查找】【键值皆有序】1840最高建筑高度

简介: 【二分查找】【键值皆有序】1840最高建筑高度

本文涉及的基础知识点

二分查找算法合集 键值皆有序

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]时:

image.png

转移过程需要计算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++**实现。

相关文章
|
算法
|
4月前
|
算法 索引
【算法】二分算法——山脉数组的峰顶索引
【算法】二分算法——山脉数组的峰顶索引
|
7月前
|
算法 测试技术 C#
【并集查找 图论 位运算】3108. 带权图里旅途的最小代价
【并集查找 图论 位运算】3108. 带权图里旅途的最小代价
|
6月前
|
算法 C语言
详解用二分法查找有序数据中的指定数字
详解用二分法查找有序数据中的指定数字
46 1
|
7月前
|
算法 测试技术 Serverless
【二分查找】【C++算法】378. 有序矩阵中第 K 小的元素
【二分查找】【C++算法】378. 有序矩阵中第 K 小的元素
|
7月前
|
算法 测试技术 C#
【键值皆有序map 线段树 数学 】100240. 最小化曼哈顿距离
【键值皆有序map 线段树 数学 】100240. 最小化曼哈顿距离
|
7月前
|
机器学习/深度学习 移动开发
【归并排序】【图论】【动态规划】【 深度游戏搜索】1569将子数组重新排序得到同一个二叉搜索树的方案数
【归并排序】【图论】【动态规划】【 深度游戏搜索】1569将子数组重新排序得到同一个二叉搜索树的方案数
|
7月前
|
算法 程序员 测试技术
【算法训练-二分查找 二】【旋转二分】旋转排序数组的最小数字、旋转排序数组的指定数字
【算法训练-二分查找 二】【旋转二分】旋转排序数组的最小数字、旋转排序数组的指定数字
46 0
【算法训练-二分查找 二】【旋转二分】旋转排序数组的最小数字、旋转排序数组的指定数字
|
7月前
leetcode-540:有序数组中的单一元素
leetcode-540:有序数组中的单一元素
39 0
|
算法 测试技术 C#
C++二分查找算法:有序矩阵中的第 k 个最小数组和(二)
C++二分查找算法:有序矩阵中的第 k 个最小数组和