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;
}