题解/算法 {3102. 最小化曼哈顿距离}

题解/算法 {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
  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值