Partisi dan Restrukturisasi

9

Dengan diberikan dua bentuk yang berdekatan dari area yang sama, tentukan cara optimal untuk membagi bentuk pertama menjadi jumlah minimum segmen yang berdekatan sehingga dapat disusun ulang untuk membentuk bentuk kedua. Dengan kata lain, temukan jumlah minimum segmen yang diperlukan yang dapat membentuk kedua bentuk tersebut.

"Berdampingan" berarti bahwa setiap kotak dalam bentuk dapat dicapai dari kotak lain dengan berjalan melintasi tepi. Bentuk dan segmen diizinkan memiliki lubang.

"Tata Ulang" berarti Anda memindahkan segmen; Anda dapat menerjemahkan, memutar, dan memantulkannya.

Bentuknya terkandung pada kotak; dengan kata lain, setiap bentuk terdiri dari kumpulan kuadrat satuan yang disatukan dengan sudut / tepinya.

Spesifikasi Input

Input akan diberikan dalam beberapa format yang masuk akal - daftar poin, array string yang mewakili setiap kisi, dll. Anda juga dapat mengambil ukuran kisi jika diminta. Grid akan memiliki dimensi yang sama dan dua bentuk dijamin memiliki area yang sama, dan area tersebut akan positif.

Spesifikasi Output

Keluaran seharusnya hanya bilangan bulat positif tunggal. Perhatikan bahwa akan selalu ada jawaban positif karena dalam skenario kasus terburuk, Anda hanya membagi bentuk menjadi Nkotak satuan.

Contohnya

Contoh-contoh disajikan sebagai kotak dengan .mewakili bagian kosong dan #mewakili bentuk.

Kasus 1

Memasukkan

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

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

Keluaran

2

Penjelasan

Anda dapat membaginya menjadi dua blok berbentuk L dari 4:

#
###

Kasus 2

Memasukkan

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

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

Keluaran

2

Penjelasan

Anda dapat membagi bentuk seperti:

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

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

Anda juga bisa:

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

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

Kasus 3

Memasukkan

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

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

Keluaran

2

Penjelasan

A....B
AAABBB

.ABBB.
.AAAB.

(Kasing uji ini menunjukkan perlunya memutar / memantulkan bentuk untuk hasil optimal)

Kasus 4

Memasukkan

.###.
..#..

.##..
.##..

Keluaran

2

Penjelasan

Tidak masalah bagaimana Anda memilih blok, memilih 2x1 dari bentuk pertama tentu mencegah dua lainnya dari dikelompokkan bersama; dengan demikian, Anda dapat menggunakan satu 2x1 dan dua 1x1s. Namun, (terima kasih @Jonah), Anda dapat membaginya menjadi bentuk 3-blok L dan satu kotak seperti ini:

.AAB.
..A..

.AA..
.BA..
HyperNeutrino
sumber
Sandbox
HyperNeutrino
1
Masalah menarik. Dan sepertinya sulit. Apakah ada algoritma untuk ini yang lebih efisien yang brute force?
Jonah
@Jonah saya tidak yakin; Saya belum memikirkan implementasi yang efisien untuk ini. Kedengarannya seperti masalah DS.
HyperNeutrino
Juga, mungkin layak menambahkan lebih banyak test case untuk masalah yang kompleks ini.
Jonah
@Jonah Saran bagus, terima kasih.
HyperNeutrino

Jawaban:

6

Python 3.6 , 799 791 byte

7 byte disimpan oleh Jonathan Frech dan motavica

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))

Cobalah online!

Pemakaian

A(s, t)mengambil dua bentuk di mana setiap bentuk diberikan oleh daftar x, yposisi kisi.

Fungsi pembantu untuk mengonversi representasi grafis ke daftar posisi adalah di bawah ini:

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]]

Contoh:

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

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

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

Penjelasan

Algoritma yang digunakan di sini sedikit lebih baik daripada brute force dengan caching sub-bentuk. Untuk bentuk yang diberikan, cache semua cara untuk membagi bentuk itu menjadi dua bentuk yang bersebelahan, saya kemudian menormalkan bentuk-bentuk ini (menggeser koordinat sehingga dimulai pada asal, kemudian menemukan rotasi / refleksi dari itu yang digunakan dalam cache) dan simpan di cache untuk mencari dengan cepat nanti. Semua sub-bentuk kemudian memiliki sub-bentuk di-cache juga sampai sampai ke bentuk blok tunggal.

Sub-bentuk ini dihasilkan dengan mengonversinya menjadi daftar adjacency grafik dan menggunakan BFS untuk menghasilkan semua sub-grafik. Kami kemudian dapat memfilter subgraph ini ke yang simpul yang tidak termasuk adalah komponen yang terhubung. Menentukan apakah grafik terhubung dilakukan dengan BFS lain.

Setelah cache selesai, solusinya ditemukan dengan membandingkan dua bentuk untuk menemukan sub-bentuk yang sama. Setelah memiliki daftar sub-bentuk ini, diperlukan sepasang sub-bentuk yang tersisa setelah menghapus bentuk bersama dan secara rekursif menerapkan algoritma yang sama lagi untuk menemukan jumlah minimum blok yang diperlukan untuk merekonstruksi bentuk. Ini kemudian mengembalikan sub-bentuk dengan minimum semua nilai-nilai itu dan kami memiliki solusi kami.

Saya telah meletakkan versi beranotasi di bawah ini untuk menjelaskan apa yang dilakukan setiap baris.

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))
Cameron Aavik
sumber
1
if s==t elsemungkin bisa dibalik, memungkinkan penggantian !=ke ^.
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))bisa jadi lambda s,t:U(s)or U(t)or V(P(s),P(t)), menghemat tiga byte.
Jonathan Frech
1
s.union(*[g[n]for n in s])~>s.union(*map(g.get,s))
movatica