[英]How can you find the cuboid with the greatest volume in a heightmap? (with low complexity)
我需要找到包含在 2D 高度圖中的體積最大的長方體。 高度圖是一個大小為w*d
的數組,其中w
是寬度, h
是高度, d
是深度。 在 C 中,這看起來像:
unsigned heightmap[w][d]; // all values are <= h
我已經知道有一個簡單的算法可以用O(w*d*h)
復雜度解決這個問題。 但是,我懷疑那里有更優化的方法。 它的工作原理如下,在pythonic偽代碼中:
resultRectangle = None
resultHeight = None
resultVolume = -1
# iterate over all heights
for loopHeight in range(0, h):
# create a 2D bitmap from our heightmap where a 1 represents a height >= loopHeight
bool bitmap[w][d]
for x in range(0, w):
for y in range(0, d):
bitmap[x][y] = heightmap[x][y] >= loopHeight
# obtain the greatest-volume cuboid at this particular height
maxRectangle = maxRectangleInBitmap(bitmap)
volume = maxRectangle.area() * loopHeight
# compare it to our current maximum and replace it if we found a greater cuboid
if volume > resultVolume:
resultHeight = loopHeight
resultVolume = volume
resultRectangle = maxRectangle
resultCuboid = resultRectangle.withHeight(resultHeight)
在矩形中找到所有1
中最大的區域是一個已知的問題,每個像素的復雜度為O(1)
或在我們的例子中為O(w*d)
。 因此,朴素方法的總復雜度為O(w*h*d)
。
正如我已經說過的,我想知道我們是否可以克服這種復雜性。 也許我們可以通過更智能地搜索高度而不是“蠻力”所有高度來將其簡化為O(w*d * log(h))
。
Evgeny Kluev 對在 NxNxN 二進制數組中查找僅包含 1 的最大長方體這個問題的答案似乎采用了類似的方法,但它錯誤地(?)假設我們會在這些高度找到的體積形成單峰函數。 如果是這樣,我們可以使用黃金分割搜索更智能地選擇高度,但我認為我們不能。
這是一個想法,有一個重要的假設。 偽代碼:
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
我有直覺但無法證明的假設是 RR 的平均大小為 O(1)。
編輯:澄清“分裂”,如果我們在點 p 分裂:
AAAAADFFF
AAAAADFFF
AAAAADFFF
BBBBBpGGG
CCCCCEHHH
CCCCCEHHH
我們生成新的矩形,包括:ABC、CEH、FGH、ADF,並將它們添加到 R。
好的,再來一張。 大多數“肉”都在go
函數中。 它使用與我的其他答案相同的“拆分”概念,但使用帶有記憶化的自頂向下動態編程。 rmq2d
實現二維范圍最小查詢。 對於 1000x1000 大小,大約需要 30 秒(使用 3GB 內存時)。
#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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.