Algorithm 如何在高度图中找到体积最大的长方体?(复杂度低)

Algorithm 如何在高度图中找到体积最大的长方体?(复杂度低),algorithm,multidimensional-array,3d,voxel,heightmap,Algorithm,Multidimensional Array,3d,Voxel,Heightmap,我需要找到体积最大的长方体,包含在二维高度图中。 高度图是一个大小为w*d的数组,其中w是宽度,h是高度,d是深度。 在C中,这将沿着以下路线进行: unsigned heightmap[w][d];//所有值均为=回路高度 布尔位图[w][d] 对于范围(0,w)内的x: 对于范围(0,d)内的y: 位图[x][y]=高度贴图[x][y]>=循环高度 #在这个特定高度获得最大体积的长方体 maxRectangle=maxRectangleInBitmap(位图) volume=maxRecta

我需要找到体积最大的长方体,包含在二维高度图中。 高度图是一个大小为
w*d
的数组,其中
w
是宽度,
h
是高度,
d
是深度。 在C中,这将沿着以下路线进行:

unsigned heightmap[w][d];//所有值均为=回路高度
布尔位图[w][d]
对于范围(0,w)内的x:
对于范围(0,d)内的y:
位图[x][y]=高度贴图[x][y]>=循环高度
#在这个特定高度获得最大体积的长方体
maxRectangle=maxRectangleInBitmap(位图)
volume=maxRectangle.area()*loopHeight
#将其与当前的最大值进行比较,如果我们发现一个更大的长方体,则将其替换
如果体积>结果体积:
resultHeight=loopHeight
结果体积=体积
resultRectangle=maxRectangle
resultCuboid=resultRectangle.withHeight(resultHeight)
在一个矩形中找到所有
1
的最大面积是一个已知的问题,即
O(1)
每像素的复杂度或
O(w*d)
。 因此,朴素方法的总复杂性是
O(w*h*d)

正如我已经说过的,我想知道我们是否能克服这种复杂性。 也许我们可以通过更智能地搜索高度,而不是“暴力强迫”所有高度,从而将其归结为
O(w*d*log(h))

Evgeny Kluev对这个问题的回答似乎采取了类似的方法,但它错误地(?)假设了我们在这些高度处发现的体积形成了单峰函数。
如果是这样的话,我们可以使用黄金分割搜索更明智地选择高度,但我认为我们做不到。

这里有一个想法,有一个重要的假设。伪代码:

P <- points from heightmap sorted by increasing height.
R <- set of rectangles. All maximal empty sub-rectangles for the current height.
R.add(Rectangle(0,0,W,H)
result = last_point_in(P).height()
foreach(p in P):
   RR <- rectangles from R that overlap P (can be found in O(size(RR)), possibly with some logarithmic factors)
   R = R - RR
   foreach(r in RR)
       result = max(result, r.area() * p.height())
       split up r, adding O(1) new rectangles to R.
return result
我们生成新的矩形,包括: ABC、CEH、FGH、ADF,并将它们添加到R中。

好的,另取一个。大多数“肉”在
go
功能中。它使用了与我的另一个答案相同的“拆分”概念,但使用了自上而下的动态编程和记忆<代码>rmq2d实现二维范围最小查询。对于1000x1000大小,大约需要30秒(同时使用3GB内存)

#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
constexpr int ilog2(int x){
返回31-内置clz(x);
}
常数int MAX_DIM=100;
模板
结构rmq2d{
结构点{
int x,y;
点():x(0),y(0){}
点(intx,inty):x(x),y(y){
};
typedef点阵列[MAX_DIM][ilog2(MAX_DIM)+1][MAX_DIM];
int h,logh;
int-w,logw;
向量v;
数组_t*A;
rmq2d(){A=nullptr;}
rmq2d和操作员=(常数rmq2d和其他){
断言(sizeof(point)==8);
如果(this==&other)返回*this;
如果(!A){
A=新阵列[ilog2(最大尺寸)+1];
}
v=其他。v;
h=其他。h;
logh=other.logh;
w=其他。w;
logw=other.logw;
memcpy(A,other.A,(ilog2(MAX_DIM)+1)*sizeof(array_t));
归还*这个;
}
rmq2d(施工rmq2d和其他){
A=空PTR;
*这个=其他;
}
~rmq2d(){
删除[]A;
}
T查询(点pos){
返回v[pos.y][pos.x];
}
rmq2d(矢量和v):v(v){
A=新阵列[ilog2(最大尺寸)+1];
h=(int)v.size();
logh=ilog2(h)+1;
w=(int)v[0].size();
logw=ilog2(w)+1;

对于(int y=0;yi如果高度非常高,您可以迭代
sqrt(h)
,这将为您提供(i)解的下限和上限,以及(ii)高度部分的子集(每个大小
sqrt(h)
)“天真地”检查。在最坏的情况下,复杂性保持不变,但在实践中,这肯定会运行得稍快。@m.raynal我不完全确定你的意思。你的意思是省略上面的数据
sqrt(h)
?这可能不会产生最佳解决方案。在任何给定高度都可以找到最佳解决方案,除非它遵循我还不知道的模式。高度图可能是完全平坦的,高度为1,使此平面在
h=1
处成为最佳解决方案。但它也可能是1x1列,使最大m体积是最大高度。因此,如果有一种方法可以忽略高度,它不能只是通过任意跳过高度。我的意思是,在2个高度之间找到的最大长方体
h1
肯定小于
w1*d1*h2
,其中
w1*d1
表示高度
h1
1
的最大面积,当然,至少与
w1*d1*h1
一样大。这允许您绑定最佳解决方案并放弃一些潜在的高度(即使在最坏的情况下,您最终将迭代所有可能的高度)。我的意思是,您可以按如下方式进行迭代,而不是迭代
h
的所有可能值:
对于h=0;h@m.raynal关于上限,您是对的,这将允许您在从上到下迭代时提前取消。但是,下限不是
w1*d1*h1
,因为位图是由所有的列都有一个高度“<代码> >=环高。更少的列满足更高的要求,所以在上行时可以找到更小的体积。考虑一个具有非常宽的基部的形状和一个从其延伸的1x1列。当增加高度时,没有一个基可以满足要求,而最大的长方体正好。作为本栏的一部分,因此体积很小。我不明白。请您帮助我理解为什么不在
O(n^2)
中的高度图中迭代给定的实际高度(即迭代
w*d
),并检查每个高度图是否
w*d*map[w][d]
大于全局最大值?有些事情我不明白。
R
是否已经用子矩形初始化,还是开始时为空?
AAAAADFFF
AAAAADFFF
AAAAADFFF
BBBBBpGGG
CCCCCEHHH
CCCCCEHHH
#include <iostream>
#include <vector>
#include <cassert>
#include <set>
#include <tuple>
#include <memory.h>
#include <limits.h>

using namespace std;

constexpr int ilog2(int x){
    return 31 - __builtin_clz(x);
}

const int MAX_DIM = 100;




template<class T>
struct rmq2d{
    struct point{
        int x,y;

        point():x(0),y(0){}
        point(int x,int y):x(x),y(y){}
    };
    typedef point array_t[MAX_DIM][ilog2(MAX_DIM)+1][MAX_DIM];

    int h, logh;
    int w, logw;
    vector<vector<T>> v;

    array_t *A;

    rmq2d(){A=nullptr;}

    rmq2d &operator=(const rmq2d &other){
        assert(sizeof(point)==8);
        if(this == &other) return *this;
        if(!A){
            A = new array_t[ilog2(MAX_DIM)+1];
        }
        v=other.v;
        h=other.h;
        logh = other.logh;
        w=other.w;
        logw=other.logw;
        memcpy(A, other.A, (ilog2(MAX_DIM)+1)*sizeof(array_t));
        return *this;
    }

    rmq2d(const rmq2d &other){
        A = nullptr;
        *this = other;
    }

    ~rmq2d(){
        delete[] A;
    }


    T query(point pos){
        return v[pos.y][pos.x];
    }

    rmq2d(vector<vector<T>> &v) : v(v){
        A = new array_t[ilog2(MAX_DIM)+1];
        h = (int)v.size();
        logh = ilog2(h) + 1;
        w = (int)v[0].size();
        logw = ilog2(w) + 1;

        for(int y=0; y<h; ++y){
            for(int x=0;x<w;x++) A[0][y][0][x] = {x, y};

            for(int jx=1; jx<logw; jx++){
                int sz = 1<<(jx-1);
                for(int x=0; x+sz < w; x++){
                    point i1 = A[0][y][jx-1][x];
                    point i2 = A[0][y][jx-1][x+sz];
                    if(query(i1) < query(i2)){
                        A[0][y][jx][x] = i1;
                    }else{
                        A[0][y][jx][x] = i2;
                    }
                }
            }
        }
        for(int jy=1; jy<logh; ++jy){
            int sz = 1<<(jy-1);
            for(int y=0; y+sz<h; ++y){
                for(int jx=0; jx<logw; ++jx){
                    for(int x=0; x<w; ++x){
                        point i1 = A[jy-1][y][jx][x];
                        point i2 = A[jy-1][y+sz][jx][x];
                        if(query(i1) < query(i2)){
                            A[jy][y][jx][x] = i1;
                        }else{
                            A[jy][y][jx][x] = i2;
                        }

                    }
                }
            }
        }
    }

    point pos_q(int x1, int x2, int y1, int y2){
        assert(A);
        int lenx = ilog2(x2 - x1);
        int leny = ilog2(y2 - y1);

        point idxs[] = {
                 A[leny][y1][lenx][x1],
                 A[leny][y2-(1<<leny)][lenx][x1],
                 A[leny][y1][lenx][x2-(1<<lenx)],
                 A[leny][y2-(1<<leny)][lenx][x2-(1<<lenx)]
                };
        point ret = idxs[0];
        for(int i=1; i<4; ++i){
            if(query(ret) > query(idxs[i])) ret = idxs[i];
        }
        return ret;

    }

    T val_q(int x1, int x2, int y1, int y2){
        point pos = pos_q(x1,x2,y1,y2);
        return v[pos.y][pos.x];
    }
};

rmq2d<long long> rmq;

set<tuple<int, int, int ,int>> cac;
vector<vector<long long>> v(MAX_DIM-5,vector<long long>(MAX_DIM-5,0));

long long ret = 0;
int nq = 0;

void go(int x1, int x2, int y1, int y2){
    if(x1 >= x2 || y1>=y2) return;

    if(!cac.insert(make_tuple(x1,y1,x2,y2)).second) return;
    ++nq;

    auto p = rmq.pos_q(x1, x2, y1, y2);
    long long cur = v[p.y][p.x]*(x2-x1)*(y2-y1);
    if(cur > ret){
        cout << x1 << "-" << x2 << ", " << y1 << "-" << y2 << " h=" << v[p.y][p.x] <<  " :" << cur << endl;
        ret = cur;
    }

    go(p.x+1, x2, y1, y2);
    go(x1, p.x, y1, y2);
    go(x1, x2, p.y+1, y2);
    go(x1, x2, y1, p.y);
}


int main(){
    int W = (int)v[0].size();
    int H=(int)v.size();
    for(int y=0; y<H;++y){
        for(int x=0; x<W; ++x){
            v[y][x] = rand()%10000;
        }
    }
    rmq = rmq2d<long long>(v);
    go(0,W, 0, H);
    cout << "nq:" << nq << endl;
}