题解/算法 {3102. 最小化曼哈顿距离}
@LINK: https://leetcode.cn/problems/minimize-manhattan-distances/description/
;
如果是第一次遇到曼哈顿距离的题目, 还是挺难的… 因为你可能认为 这是个几何的题目 然后去研究他的几何空间性质 就误入歧途了, 因为 曼哈顿距离 并不是欧氏距离, 你去研究空间 他是欧式空间 欧氏距离, 并不是曼哈顿距离, 因此 你能研究的空间 无非就是笛卡尔坐标系, 因为并不存在 像欧式空间那样形象具体的曼哈顿空间…
正确做法是 曼哈顿距离的一个经典算法: 转换为切比雪夫距离;
设任一点X
他对应的切比雪夫坐标为X1 = (X.x+X.y, X.x-X.y)
, 那么若干个X点的最大曼哈顿距离 就等于 若干个对应的X1
点的最大切比雪夫距离;
.
因此: 最大的曼哈顿距离是max{ (X1.x最大值 - X1.x最小值), (X1.y最大值 - X1.y最小值)}
(即在各个维度独立的 求一下最大值和最小值即可);
那么你要删除一个点, 这个点 肯定是{X1.x最大值, X1.x最小值, X1.y最大值, X1.y最小值}
里的其中一个; 因此有以下几个算法:
1(O(N*logN)
: 令X(multiset): 维护所有X1.x坐标; Y(multiset): 维护所有X1.y坐标
, 遍历每个点 将该点从X,Y
里删除掉, 求此时max( (X最大-X最小), (Y最大-Y最小))
更新答案, 然后再将该点恢复添加到X,Y
里面; 这种写法最容易;
2(O(2*N)
): 令X(multiset): 维护所有X1.x坐标的{最小,次小,最大,次大}; Y(multiset): 维护所有X1.y坐标的{最小,次小,最大,次大}
; 遍历每个点 将该点从X,Y
里面去掉, 然后求max( (X最大-X最小), (Y最大-Y最小))
; 这种写法 代码难度中等;
3(O(N)
): 令X(multiset): 维护所有X1.x坐标的{最小,次小,最大,次大}; Y(multiset): 维护所有X1.y坐标的{最小,次小,最大,次大}
; 枚举删除{X最大,X最小,Y最大,Y最小}
, 注意 比如枚举删除X最大
这个点 可能同时是Y最大/最小
因此你还需要删除Y最大/最小
; 这个算法 效率最高 但代码难度很大… 以下模板代码 就是用的这个算法;
给定若干个点, 选择删除任一点后 求任意两点的曼哈顿距离的最大值的最小值
代码
template< class _TypePoint_> class ___MaximalManhattanDistance_AfterRemoveOnePoint{
ASSERT_MSG_("确保`_TypePoint_`相加减 不会溢出");
public:
using __TypeSet_ = std::multiset< std::pair<_TypePoint_, std::pair<_TypePoint_,_TypePoint_> > >;
__TypeSet_ __Sum; // `x + y`
__TypeSet_ __Dif; // `x - y`
void Initialize(){
__Sum.clear(); __Dif.clear();
}
void Initialize_addPoint( _TypePoint_ _x, _TypePoint_ _y){
__Sum.insert( {_x+_y, {_x,_y}});
__Dif.insert( {_x-_y, {_x,_y}});
while( __Sum.size() > 4){ __Sum.erase( std::next(std::next(__Sum.begin())));}
while( __Dif.size() > 4){ __Dif.erase( std::next(std::next(__Dif.begin())));}
}
std::tuple<_TypePoint_, std::pair<_TypePoint_,_TypePoint_>, std::pair<_TypePoint_,_TypePoint_>, std::pair<_TypePoint_,_TypePoint_> > Work(){
//< 返回值: {删除某一点后最大曼哈顿距离的最小值, 删除的点, 新图中最大曼哈顿距离的端点, 新图中最大曼哈顿距离的端点};
ASSERT_SYSTEM_( __Sum.size() > 1);
_TypePoint_ Dist = std::numeric_limits<_TypePoint_>::max();
std::pair<_TypePoint_,_TypePoint_> P0, P1, P2;
auto const ma_sum = std::prev(__Sum.end());
auto const mi_sum = __Sum.begin();
auto const ma_dif = std::prev(__Dif.end());
auto const mi_dif = __Dif.begin();
#define __FIND_MAX_( _ma, _mi) if( _ma->first - _mi->first > dist){ dist = _ma->first - _mi->first; ma = _ma; mi = _mi;}
if( ma_sum->first - mi_sum->first > ma_dif->first - mi_dif->first){
{ // 删除`Max_sum`, 找*最大的*曼哈顿距离;
_TypePoint_ dist = std::numeric_limits<_TypePoint_>::lowest();
typename __TypeSet_::iterator ma, mi;
__FIND_MAX_( std::prev(ma_sum), mi_sum);
if( ma_sum->second == ma_dif->second){ __FIND_MAX_( std::prev(ma_dif), mi_dif);}
else if( ma_sum->second == mi_dif->second){ __FIND_MAX_( ma_dif, std::next(mi_dif));}
else{ __FIND_MAX_( ma_dif, mi_dif);}
if( dist < Dist){ Dist = dist; P0=ma->second; P1=mi->second; P2=ma_sum->second;}
}
{ // 删除`Min_sum`
_TypePoint_ dist = std::numeric_limits<_TypePoint_>::lowest();
typename __TypeSet_::iterator ma, mi;
__FIND_MAX_( ma_sum, std::next(mi_sum));
if( mi_sum->second == ma_dif->second){ __FIND_MAX_( std::prev(ma_dif), mi_dif);}
else if( mi_sum->second == mi_dif->second){ __FIND_MAX_( ma_dif, std::next(mi_dif));}
else{ __FIND_MAX_( ma_dif, mi_dif);}
if( dist < Dist){ Dist = dist; P0=ma->second; P1=mi->second; P2=mi_sum->second;}
}
}
else{
{ // 删除`Max_dif`
_TypePoint_ dist = std::numeric_limits<_TypePoint_>::lowest();
typename __TypeSet_::iterator ma, mi;
__FIND_MAX_( std::prev(ma_dif), mi_dif);
if( ma_dif->second == ma_sum->second){ __FIND_MAX_( std::prev(ma_sum), mi_sum);}
else if( ma_dif->second == mi_sum->second){ __FIND_MAX_( ma_sum, std::next(mi_sum));}
else{ __FIND_MAX_( ma_sum, mi_sum);}
if( dist < Dist){ Dist = dist; P0=ma->second; P1=mi->second; P2=ma_dif->second;}
}
{ // 删除`Min_dif`
_TypePoint_ dist = std::numeric_limits<_TypePoint_>::lowest();
typename __TypeSet_::iterator ma, mi;
__FIND_MAX_( ma_dif, std::next(mi_dif));
if( mi_dif->second == ma_sum->second){ __FIND_MAX_( std::prev(ma_sum), mi_sum);}
else if( mi_dif->second == mi_sum->second){ __FIND_MAX_( ma_sum, std::next(mi_sum));}
else{ __FIND_MAX_( ma_sum, mi_sum);}
if( dist < Dist){ Dist = dist; P0=ma->second; P1=mi->second; P2=mi_dif->second;}
}
}
#undef __FIND_MAX_
return {Dist, P2, P0, P1};
}
}; // ___MaximalManhattanDistance_AfterRemoveOnePoint