题解/算法 {LCP 74. 最强祝福力场}

https://leetcode.cn/problems/xepqZ5/, @MARK_2

由于只有 N = 100 N=100 N=100的矩阵, 可以直接暴力枚举; 即我们考虑, 被重叠次数最多的这个矩阵T (他可能是一条线 或一个点, 都认为是矩阵), 有什么性质;

-{ 错误思路

这个矩阵T的顶点, 至少有1个 是原矩阵的顶点; (即原矩阵共 N ∗ 4 N*4 N4个顶点, 其中就是T的顶点);

这是错误的, 仅用3个矩阵 就可以构成一个重叠了3次的矩阵区域, 该区域的4个顶点 都不是原矩阵的顶点;

-}

矩阵T的4条边, 一定都是原矩阵的边;
. 设矩阵的左边为 L L L, 上边为 U U U, 左上角为 L U LU LU (即 L , U L,U L,U的交点); 那么, L L L一定来自于原矩阵的某个竖边, U U U一定来自原矩阵的某个横边;
. 也就是, 矩阵T的4个顶点 一定是原矩阵的某个横边与某个竖边的交点; 因此, 我们枚举原矩阵的所有交点, 其实一定含有 答案矩阵T的顶点;

因此, 枚举原矩阵的所有竖边 A A A ( 2 N 2N 2N个), 再枚举原矩阵的所有横边 B B B ( 2 N 2N 2N个);
. 判断如果 A , B A,B A,B并不相交, 则continue; (这是个优化, 会大大提升时间);
. 得到 A , B A,B A,B的交点 C C C, 再 O ( N ) O(N) O(N)遍历所有矩阵 看有多少矩阵包含了该点 C C C;

该算法是 O ( N 3 ) O(N^3) O(N3)的;

-{ 这里提一个技巧; @MARK_0;

原矩阵的四个顶点, 他的坐标会是浮点数, 因为 ( x ± d / 2 , y ± d / 2 ) (x \pm d/2, y \pm d/2) (x±d/2,y±d/2), 当 d d d为奇数, 那么 d / 2 d/2 d/2就是个浮点数;

我们会涉及到大量的比较操作, 浮点数的比较 (虽然这道题你直接使用a<=b是没问题的) 通过要考虑精度问题, 能避开浮点数最好避开;

我们对整个坐标系 (以原点为中心) 放大1倍后, 不会影响答案 (原来答案区域面积为S, 现在会变成 4 S 4S 4S), 答案矩阵的顶点 依然是原矩阵的交点;
. 原来是(x, y, d) (正方形的中心点在(x,y), 边长为d), 现在是(2x, 2y, 2d);

-}

@DELIMITER

这里再介绍一种算法, 他是 O ( N 2 ) O(N^2) O(N2)的; @MARK_1;

本质上他也是基于上面的结论; 对每个矩阵, 把他的4条边 无限的延伸下去 形成网格线, 最终, 这个坐标系 就变成了网格, 那么有多少个网格点呢 (网格点: 即2条网格线的交点), 最多有 2 N ∗ 2 N 2N * 2N 2N2N个;

此时答案矩阵, 他的顶点 一定是网格点; (这个是上面分析过的)
. 但要注意, 他的内部 也可能有网格点; 假如该矩阵被覆盖了K次, 那么, 对于任意在该矩阵上的网格点 (可能在内部, 也可以在边上), 每个网格点 都是被覆盖了K次;

但是, 由于这些网格点 坐标是(-3e9, 3e9) (坐标系放大1倍后), 我们对他进行二维离散化, 即这些网格点, 都变成了( {0,...,200}, {0, 200})的范围;
. 然后, 对于一个矩阵 他所覆盖的网格点 对应离散化以后 一定是一个连续的 (比如他覆盖的网格点是(1,1) (1,2) (1,3) (2,1) (2,2) (2,3)), 因此进行2D的差分操作, 将该矩阵所覆盖的网格点 都+= 1;
. 最后, 枚举所有的网格点, 求他的覆盖次数;

int fieldOfGreatestBlessing(vector<vector<int>>& F) {
    int n = F.size();
    __HashDiscretization< long long> H( 2 * n), V( 2 * n);
    for( int i = 0; i < n; ++i){
        auto l = (long long)F[ i][ 0] * 2 - F[ i][ 2];
        auto r = (long long)F[ i][ 0] * 2 + F[ i][ 2];
        auto d = (long long)F[ i][ 1] * 2 - F[ i][ 2];
        auto u = (long long)F[ i][ 1] * 2 + F[ i][ 2];
        H.Add( l), H.Add( r), V.Add( d), V.Add( u);
    }
    H.Discretize(), V.Discretize();
    __DifferentialArray_2D< int, 205> D( H.Get_size(), H.Get_size(), V.Get_size());
    //< 注意, 第一参数是`H.size`, 不是`V.size`; `205`是指的Y (即第2维度)的大小, 第1维度的大小 是用户指定的;
    for( auto & i : F){
        long long dx = i[0] * 2, dy = i[1] * 2, dd = i[2];
        auto l = dx - dd, r = dx + dd, u = dy + dd , d = dy - dd;
        l = H.Get_hash( l), r = H.Get_hash( r);
        u = V.Get_hash( u), d = V.Get_hash( d);
        D.Add_area( l, d, r, u, 1);
    }
    D.Restore();
    int Ans = 0;
    for( int i = 0; i < (int)H.Get_size(); ++i){
        for( int j = 0; j < (int)V.Get_size(); ++j){
            Ans = max( Ans, D.Get_restoredValue( i, j));
        }
    }
    return Ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值