10

私は1週間以上問題の解決策を見つけようとしてきましたが、ミリオンイテレーションプログラムよりも優れたものを見つけることができなかったので、誰かに助けを求める時が来たと思います。

3D配列があります。たとえば、地面について話していて、最初のレイヤーはサーフェスです。別の層は地下の床です。私は最も深い道の長さ、地下の孤立した洞窟の数、そして最大の洞窟のサイズを見つけなければなりません。

これが私の問題の視覚化です。

Input:
5 5 5 // x, y, z
xxxxx
oxxxx
xxxxx
xoxxo
ooxxx

xxxxx
xxoxx

and so...

Output:
5 // deepest path - starting from the surface
22 // size of the biggest cave
3 // number of izolated caves (red ones) (izolated - cave that doesn't reach the surface)

なお、2階の赤いセルは緑のセルの隣に配置されていますが、斜めに配置されているため、同じ洞窟ではありません。これを行うための最良の方法は、再帰的アルゴリズム「分割統治」を使用することかもしれないと言われましたが、それがどのように見えるかは本当にわかりません。

4

4 に答える 4

4

O(N)でできると思います。入力を解析するときは、各ノードに 0 に初期化された「caveNumber」を割り当てます。洞窟を訪れるたびに有効な番号に設定します。

CaveCount = 0, IsolatedCaveCount=0
AllSizes = new Vector.
For each node, 
   ProcessNode(size:0,depth:0);

ProcessNode(size,depth):
   If node.isCave and !node.caveNumber 
       if (size==0) ++CaveCount
       if (size==0 and depth!=0) IsolatedCaveCount++
       node.caveNumber = CaveCount
       AllSizes[CaveCount]++
       For each neighbor of node, 
            if (goingDeeper) depth++
            ProcessNode(size+1, depth).

最悪の場合、各ノードに 7 回アクセスします。1 回は外側のループから、場合によっては 6 つの隣接ノードそれぞれから 1 回です。ただし、後で caveNumber が設定され、それを無視するため、それぞれの作業は 1 回だけです。

再帰的な ProcessNode 呼び出しに深さパラメーターを追加し、下位の隣人を訪問したときにのみ増加させることで、深さの追跡を行うことができます。

于 2012-10-19T16:27:46.103 に答える
2

以下に示すソリューション(Pythonプログラムとして)は時間内に実行されますO(n lg*(n))。ここで、lg*(n)は、互いに素な集合のフォレストでのユニオン操作に関連付けられることが多い、ほぼ一定の反復対数関数です。

すべてのセルの最初のパスで、プログラムは、 Cormen / Leiserson / Riverstのエディション1のセクション22.3(Disjoint-set forests)で説明されているように、makeset(), findset(), link(),andと呼ばれるルーチンを使用して、disjoint-setフォレストを作成します。union()セルを通過する後のパスでは、各ばらばらのフォレストのメンバーの数をカウントし、深さなどをチェックします。最初のパスは時間内O(n lg*(n))に実行され、後のパスは時間内に実行されますO(n)が、単純なプログラム変更によって、一部のパスが実行される可能性がありますO(c)O(b)合計b個のセルを持つc個の洞窟。

以下に示すコードは、前の回答に含まれるエラーの影響を受けないことに注意してください。前の回答の擬似コードには、次の行が含まれています。この行
if (size==0 and depth!=0) IsolatedCaveCount++
のエラーは、表面に接続された洞窟に地下の隆起した枝がある可能性があることです。もう1つの答えは、孤立した洞窟の総数に誤って追加されます。

以下に示すコードは、次の出力を生成し
Deepest: 5 Largest: 22 Isolated: 3
ます(図に示されている24のカウントは、4 + 9 + 9から22になるはずです)。

v=[0b0000010000000000100111000,   # Cave map
   0b0000000100000110001100000,
   0b0000000000000001100111000,
   0b0000000000111001110111100,
   0b0000100000111001110111101]
nx, ny, nz = 5, 5, 5
inlay, ncells = (nx+1) * ny,  (nx+1) * ny * nz
masks = []
for r in range(ny):
    masks += [2**j for j in range(nx*ny)][nx*r:nx*r+nx] + [0]
p = [-1 for i in range(ncells)]  # parent links
r = [ 0 for i in range(ncells)]  # rank
c = [ 0 for i in range(ncells)]  # forest-size counts
d = [-1 for i in range(ncells)]  # depths

def makeset(x): # Ref: CLR 22.3, Disjoint-set forests
    p[x] = x
    r[x] = 0
def findset(x):
    if x != p[x]:
        p[x] = findset(p[x])
    return p[x]
def link(x,y):
    if r[x] > r[y]:
        p[y] = x
    else:
        p[x] = y
        if r[x] == r[y]:
            r[y] += 1
def union(x,y):
    link(findset(x), findset(y))

fa = 0                          # fa = floor above
bc = 0                          # bc = floor's base cell #
for f in v:                     # f = current-floor map
    cn = bc-1                   # cn = cell#
    ml = 0
    for m in masks:
        cn += 1
        if m & f:
            makeset(cn)
            if ml & f:
                union(cn, cn-1)
            mr = m>>nx
            if mr and mr & f:
                union(cn, cn-nx-1)
            if m & fa:
                union(cn, cn-inlay)
        ml = m
    bc += inlay
    fa = f

for i in range(inlay):
    findset(i)
    if p[i] > -1:
        d[p[i]] = 0
for i in range(ncells):
    if p[i] > -1:
        c[findset(i)] += 1
        if d[p[i]] > -1:
            d[p[i]] = max(d[p[i]], i//inlay)
isola = len([i for i in range(ncells) if c[i] > 0 and d[p[i]] < 0])
print "Deepest:", 1+max(d), "  Largest:", max(c), "  Isolated:", isola
于 2012-10-19T20:45:50.597 に答える
2

「連結成分」の問題を解決しているようです。3D配列をビット配列に変換できる場合(たとえば、0 =岩盤、1 =洞窟、またはその逆)、画像処理で使用される手法を適用して、前景または背景の数と寸法を見つけることができます。

通常、このアルゴリズムは2D画像に適用され、同じ色の「連結成分」または「ブロブ」を見つけます。可能であれば、「シングルパス」アルゴリズムを見つけてください。

http://en.wikipedia.org/wiki/Connected-component_labeling

同じ手法を3Dデータに適用できます。「連結成分3D」をグーグルで検索すると、次のようなリンクが生成されます。

http://www.ecse.rpi.edu/Homepages/wrf/pmwiki/pmwiki.php/Research/ConnectedComponents

アルゴリズムが3D配列の処理を完了すると、ラベル付けされた接続された領域のリストが作成され、各領域がボクセル(画像ピクセルに類似したボリューム要素)のリストになります。次に、ラベル付けされた各領域を分析して、体積、表面への近さ、高さなどを決定できます。

これらのアルゴリズムの実装は少し難しい場合があり、最初に2D実装を試してみることをお勧めします。あまり効率的ではないかもしれませんが、2Dアルゴリズムを各レイヤーに繰り返し適用し、接続された領域を最上層から最下層に再ラベル付けすることで、3D連結成分ラベル付けアルゴリズムを作成できます。

  1. レイヤー0の場合、2D連結成分アルゴリズムを使用してすべての連結領域を検索します
  2. レイヤー1の場合、接続されているすべての領域を見つけます。
  3. レイヤー0のラベル付きピクセルがレイヤー1のラベル付きピクセルの真上にある場合は、レイヤー1のすべてのラベルをレイヤー0のラベルに変更します。
  4. レイヤーNに到達するまで、このラベル付け手法をスタック全体に繰り返し適用します。

連結成分のラベル付けで考慮すべき重要なことの1つは、領域が連結されていると見なす方法です。ビットの2D画像(または2D配列)では、隣接する要素の「4接続」領域のいずれかを考慮することができます

X 1 X
1 C 1
X 1 X

ここで、「C」は中央の要素、「1」は接続されていると見なされる隣接要素、「X」は接続されていると見なされない隣接する隣接要素を示します。もう1つのオプションは、「8接続されたネイバー」を検討することです。

1 1 1
1 C 1
1 1 1

つまり、中央のピクセルに隣接するすべての要素が接続されていると見なされます。最初は、これはより良いオプションのように聞こえるかもしれません。実際の2D画像データでは、ノイズのチェス盤パターンまたは単一ノイズピクセルの対角線ストリングが接続領域として検出されるため、通常、4接続性をテストします。

3Dデータの場合、6接続性または26接続性のいずれかを考慮することができます。6接続性は、中心ボクセルと完全な立方体面を共有する隣接ピクセルのみを考慮し、26接続性は、中心ボクセルの周囲のすべての隣接ピクセルを考慮します。「対角線上に配置」はカウントされないため、6接続で十分であるとおっしゃっています。

于 2012-10-21T15:22:06.383 に答える
1

両方が空 (洞窟の一部) の場合、(非対角) 隣接する要素が接続されているグラフとして観察できます。グラフに変換する必要はありません。通常の 3D 配列表現を使用できます。

洞窟を見つけることは、グラフ (O(N)) で接続されたコンポーネントを見つけることと同じタスクであり、洞窟のサイズはそのコンポーネントのノードの数です。

于 2012-10-19T16:27:22.540 に答える