パーティションと再構築


9

同じ領域の2つの隣接する形状が与えられた場合、最初の形状を最小数の隣接するセグメントに分割して、それらを再配置して2番目の形状を形成できるようにする最適な方法を決定します。つまり、両方の形状を形成できる、必要な最小数のセグメントを見つけます。

「隣接」とは、形状を構成するすべての正方形が、他の正方形から端を横切ることで到達できることを意味します。形状とセグメントに穴を開けることができます。

「再配置」とは、セグメントを移動することを意味します。それらを平行移動、回転、反映することができます。

形状はグリッドに含まれています。つまり、各形状は、コーナー/エッジで結合された単位正方形のコレクションで構成されます。

入力仕様

入力は、いくつかの合理的な形式で提供されます-ポイントのリスト、各グリッドを表す文字列の配列など。要求された場合は、グリッドのサイズを取得することもできます。グリッドの寸法は同じになり、2つの形状は同じ面積になることが保証され、面積は正になります。

出力仕様

出力は単一の正の整数でなければなりません。最悪の場合のシナリオでは、形状をN単位正方形に分割するだけなので、常に肯定的な回答が得られることに注意してください。

例は.、空白を#表し、形状の一部を表すグリッドとして表示されます。

事例1

入力

.....
.###.
.#.#.
.###.
.....

###..
..#..
..#..
..###
.....

出力

2

説明

4つのL字型ブロックに分割できます。

#
###

事例2

入力

#...
##..
.#..
.##.

.##.
####
....
....

出力

2

説明

次のように形状を分割できます:

A...
AA..
.A.
.BB.

.AA.
BBAA
....
....

あなたも行うことができます:

A...
AA..
.B..
.BB.

.AB.
AABB
....
....

事例3

入力

#....#
######

.####.
.####.

出力

2

説明

A....B
AAABBB

.ABBB.
.AAAB.

(このテストケースは、最適な出力のために形状を回転/反映する必要性を示しています)

事例4

入力

.###.
..#..

.##..
.##..

出力

2

説明

ブロックをどのように選択しても、最初のシェイプから2x1を選択すると、他の2つがグループ化されるのを防ぐことができます。したがって、1つの2x1と2つの1x1を使用できます。ただし、(@ Jonahに感謝)、次のように3ブロックのL形と1つの正方形に分割できます。

.AAB.
..A..

.AA..
.BA..


1
興味深い問題。そして一見難しい。ブルートフォースというより効率的なアルゴリズムはありますか?
ジョナ

@ジョナ私はよくわかりません。このための効率的な実装についてはまだ考えていません。DSの問題のように聞こえます。
HyperNeutrino

また、おそらくこの複雑な問題のテストケースを追加する価値があります。
ジョナ

@ジョナ良い提案、ありがとう。
HyperNeutrino

回答:


6

Pythonの3.6799の 791バイト

Jonathan Frechとmotavicaによって7バイト節約

B=frozenset
M=min
X={}
T=lambda s:((x,y)for y,x in s)
N=lambda s:B((x-M(s)[0],y-M(T(s))[0])for x,y in s)
G=lambda s:{(x,y):s&{(x-1,y),(x+1,y),(x,y-1),(x,y+1)}for(x,y)in s}
C=lambda g,s,i=0:i<=len(g)and(len(g)==len(s)or C(g,s.union(*map(g.get,s)),i+1))
F=lambda s:N([(-x,y)for x,y in s])
P=lambda s,i=4:M([N(s),F(s),P(F(T(s)),i-1)],key=list)if i else s
S=lambda s,t=B(),i=0:t|S(s,B().union(u|{p}for u in t for p in s-u if C(G(u|{p}),{p}))if i else B([B([next(iter(s))])]),i+1)if-~i<len(s)else t
def U(s):
 k=P(s)
 if k in X:return
 j=[t for t in S(k)if C(G(k-t),{next(iter(k-t))})];X[k]={P(t):B()for t in j}
 for t in j:X[k][P(t)]|=B([B(P(k-t))]);U(t);U(k-t)
V=lambda s,t:1+M(V(v,w)for u in B(X[s])&B(X[t])for v in X[s][u]for w in X[t][u])if s^t else 1
A=lambda s,t:U(s)or U(t)or V(P(s),P(t))

オンラインでお試しください!

使用法

A(s, t)x, yグリッド位置のリストによって各形状が与えられる2つの形状を取り込みます。

グラフィカル表現を位置のリストに変換するヘルパー関数は次のとおりです。

def H(g):
 g=g.split("\n")
 return[(x,y)for x in range(len(g[0]))for y in range(len(g))if"#"==g[y][x]]

例:

case1_1 = \
""".....
.###.
.#.#.
.###.
....."""

case1_2 = \
"""###..
..#..
..#..
..###
....."""

print(A(H(case1_1), H(case1_2))) # Prints 2

説明

ここで使用されているアルゴリズムは、サブシェイプをキャッシュするため、ブルートフォースよりも少し優れています。与えられた形状について、それはその形状を2つの隣接する形状に分割するすべての方法をキャッシュし、次にこれらの形状を正規化します(原点から始まるように座標をシフトし、次にキャッシュで使用されるその回転/反射を見つけます)。後ですばやく検索できるように、それらをキャッシュに保存します。すべてのサブシェイプは、単一のブロックシェイプになるまで、サブシェイプもキャッシュされます。

これらのサブシェイプは、グラフの隣接リストに変換し、BFSを使用してすべてのサブグラフを生成することによって生成されます。次に、これらのサブグラフをフィルタリングして、含まれていなかった頂点が接続されたコンポーネントになるようにします。グラフが接続されているかどうかの判別は、別のBFSで行われます。

キャッシュが完了した後、2つの形状を比較して、共通のサブ形状を見つけることにより、ソリューションが見つかります。サブシェイプのこれらのリストを取得すると、共通のシェイプを削除した後に残ったサブシェイプのペアを取り、同じアルゴリズムを再帰的に適用して、シェイプを再構築するために必要な最小数のブロックを見つけます。次に、これらすべての値の最小値を持つサブシェイプを返し、ソリューションが得られます。

各行が何をしているのかを説明するために、以下に注釈付きのバージョンを配置しました。

B=frozenset
M=min
# Shapes are stored as a frozenset of tuples where each tuple represents an (x, y) position
# Cache of shape partitions. This is a two-level dictionary where the outer key is a shape, and the inner key is a sub-shape where the value is a list of the shapes left when the sub-shape is removed.
# there may be multiple shapes in the inner-most list if the sub-shape appears multiple times
X={}
# Transpose list of coords (flip around diagonal axis)
T=lambda s:((x,y)for y,x in s)
# Translate shape so its upper-left corner is at the origin
N=lambda s:B((x-M(s)[0],y-M(T(s))[0])for x,y in s)
# Convert shape to graph in adjacency list form
G=lambda s:{(x,y):s&{(x-1,y),(x+1,y),(x,y-1),(x,y+1)}for(x,y)in s}
# Check if graph is connected given a set of nodes, s, known to be connected
C=lambda g,s,i=0:i<=len(g)and(len(g)==len(s)or C(g,s.union(*map(g.get,s)),i+1))
# Flip shape around vertical axis
F=lambda s:N([(-x,y)for x,y in s])
# Converts shape to the minimal reflection or rotation. rotation is equivalent to transpose then flip.
P=lambda s,i=4:M([N(s),F(s),P(F(T(s)),i-1)],key=list)if i else s
# returns all the contiguous sub-shapes of s that contain the first pos, given by next(iter(s))
S=lambda s,t=B(),i=0:t|S(s,B().union(u|{p}for u in t for p in s-u if C(G(u|{p}),{p}))if i else B([B([next(iter(s))])]),i+1)if-~i<len(s)else t
# updates the sub-shape cache, X, recursively for an input shape s 
def U(s):
 k=P(s)
 if k in X:return
 j=[t for t in S(k)if C(G(k-t),{next(iter(k-t))})];X[k]={P(t):B()for t in j}
 for t in j:X[k][P(t)]|=B([B(P(k-t))]);U(t);U(k-t)
# Gets the minimum number of partitions for two shapes
V=lambda s,t:1+M(V(v,w)for u in B(X[s])&B(X[t])for v in X[s][u]for w in X[t][u])if s^t else 1
# The function to run, will update the cache for the two input shapes then return the minimum number of partitions
A=lambda s,t:U(s)or U(t)or V(P(s),P(t))

1
if s==t elseを逆にして、!=toを置き換えることができ^ます。
Jonathan Frech

1
if i<len(s)-1else〜> if-~i<len(s)else
Jonathan Frech

1
def A(s,t):U(s);U(t);return V(P(s),P(t))lambda s,t:U(s)or U(t)or V(P(s),P(t))3バイトを節約できる可能性があります。
Jonathan Frech

1
s.union(*[g[n]for n in s])〜>s.union(*map(g.get,s))
movatica
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.