Pemecahan garis kasar nonogram

25

Latar Belakang

Nonogram , juga dikenal sebagai Picross atau Griddlers, adalah teka-teki di mana tujuannya adalah untuk menentukan apakah setiap sel pada grid 2D harus diwarnai atau dibiarkan kosong, menggunakan jumlah sel berwarna berturut-turut pada setiap baris.

Berikut ini adalah contoh puzzle Nonogram dengan solusi.

Masalahnya adalah, beberapa game / aplikasi seluler Nonogram komersial memiliki teka-teki yang tidak dapat dipecahkan dengan tangan (mis. Memiliki beberapa solusi, atau memerlukan pengulangan yang dalam). Namun, mereka juga menawarkan beberapa nyawa kepada pemain, di mana satu nyawa hilang ketika Anda mencoba untuk mewarnai sel yang jawaban yang benar kosong . Jadi sekarang saatnya untuk memaksa "puzzle" jahat itu!

Untuk menyederhanakan tugas, bayangkan hanya satu baris dengan petunjuknya dan tidak ada yang lain:

3 7 | _ _ _ _ _  _ _ _ _ _  _ _ _ _ _

The [3,7]adalah petunjuk, dan panjang garis adalah 15 sel. Karena memiliki beberapa solusi yang mungkin, kita perlu mengambil risiko beberapa nyawa untuk sepenuhnya menyelesaikan garis ini (yaitu menentukan semua sel berwarna).

Tantangan

Diberi garis dengan petunjuk (daftar bilangan bulat positif) dan panjang garis, temukan jumlah maksimum nyawa yang akan Anda hilangkan, dengan anggapan bahwa Anda brute-paksa garis dengan strategi optimal.

Perhatikan bahwa Anda selalu menebak sel berwarna . Dalam gim sebenarnya, menebak sel kosong (baik benar atau salah) tidak berpengaruh pada hidup Anda, jadi Anda tidak bisa "memecahkan" teka-teki seperti itu.

Juga, Anda dapat mengasumsikan input selalu mewakili garis Nonogram yang valid, jadi Anda tidak perlu khawatir tentang sesuatu seperti [6], 5.

Penjelasan

Mari kita lihat beberapa contoh sederhana dulu.

[1,2], 5

Ada tiga kemungkinan untuk garis ini ( Osel berwarna, sel .kosong):

O . O O .
O . . O O
. O . O O

Jika kami mencoba mewarnai sel 0 (indeks berbasis 0 dari kiri), salah satu dari yang berikut ini terjadi:

  • Sel berwarna dengan benar. Sekarang kita memiliki dua kemungkinan, dan kita dapat memilih antara sel 2 dan sel 4 untuk sepenuhnya menyelesaikan garis. Bagaimanapun, kita akan kehilangan satu nyawa dalam kasus terburuk.
  • Sel itu kosong, dan kita kehilangan nyawa. Dalam hal ini, kami telah mengidentifikasi solusi unik untuk baris ini, jadi kami selesai dengan 1 nyawa yang hilang.

Karena itu, jawabannya [1,2], 5adalah 1.

[5], 10

Pencarian biner? Nggak.

Pilihan pertama yang paling jelas adalah 4 atau 5, yang akan mengungkapkan satu kemungkinan jika itu kosong (dengan biaya 1 kehidupan). Katakanlah kita memilih 4 dulu. Jika sel 4 benar-benar berwarna, kita perluas ke kiri, yaitu coba 3, 2, 1 dan 0 sampai sebuah nyawa hilang (atau sel 0 berwarna, maka kita tidak menghabiskan hidup sama sekali). Setiap kali nyawa hilang, kita dapat secara unik menentukan solusinya, misalnya jika kita melihat sesuatu seperti ini:

_ _ X O O _ _ _ _ _

maka kita sudah tahu jawabannya adalah ini:

. . . O O O O O . .

Karena itu, jawabannya [5], 10adalah 1.

[3,7], 15

Mulai dengan sel 11, yang, jika kosong, akan segera mengungkap solusi berikut.

O O O . O O O O O O O X . . .

Kemudian coba 12, yang, jika kosong, memberikan dua kemungkinan yang dapat diselesaikan dengan biaya 1 kehidupan tambahan.

O O O . . O O O O O O O X . .
. O O O . O O O O O O O X . .

Sekarang coba 2. Jika kosong, itu mengarah ke tiga kemungkinan yang dapat dipecahkan mirip dengan [1,2], 5contoh.

. . X O O O . O O O O O O O .
. . X O O O . . O O O O O O O
. . X . O O O . O O O O O O O

Jika Anda terus meminimalkan risiko dengan cara ini, Anda dapat mencapai solusi apa pun dengan maks. 2 nyawa dihabiskan.

Uji Kasus

[1,2] 5 => 1
[2] 5 => 2
[1] 5 => 4
[] 5 => 0
[5] 10 => 1
[2,1,5] 10 => 0
[2,4] 10 => 2
[6] 15 => 2
[5] 15 => 2
[4] 15 => 3
[3,7] 15 => 2
[3,4] 15 => 3
[2,2,4] 15 => 4
[1,1,1,1,1,1,1] 15 => 2

[2,1,1,3,1] 15 => 3
[1,1,1,2,1] 15 => 5

Untuk dua kasus terakhir, strategi optimal tidak melalui blanko minimum, tetapi hanya bergerak dari kiri ke kanan (atau kanan ke kiri). Terima kasih kepada @crashoz karena menunjukkannya.

Aturan

Aturan standar berlaku. Pengajuan terpendek yang valid dalam byte menang.

Karunia

Jika seseorang datang dengan algoritma waktu polinomial (dengan bukti kebenaran), saya akan memberikan hadiah +100 untuk solusi semacam itu.

Bubbler
sumber
Untuk apa output yang diinginkan [6], 5?
Leaky Nun
Ketika Anda menebak, apakah Anda harus menebak bahwa sel itu hitam, atau bisakah Anda menebak hitam atau putih?
feersum
@ LeakyNun Ini garis yang tidak valid. Anda dapat berasumsi bahwa input selalu berupa garis Nonogram yang valid.
Bubbler
@feersum Anda selalu menebak sel berwarna. Dalam gim sebenarnya, menebak sel kosong (baik benar atau salah) tidak berpengaruh pada kehidupan Anda, jadi Anda tidak bisa mendapatkan umpan balik darinya.
Bubbler
Tantangan yang fantastis
Enrico Borba

Jawaban:

19

Ruby , 85 byte

f=->l,n,s=n-l.sum-l.size+1{*a,b=l;b&&s>0?(a[0]?1+f[a,n-b-2,s-1]:(n.to_f/b).ceil-1):0}

Cobalah online!

Penjelasan

l=[l1,l2,...,lx]xn

lx
nlx
nlx1+f(l,nlx)
1+f(l~,nlx2)l~l

f(l,n)={0,if 1xli+x1nnlx1if x=11+max{f(l,nlx)f(l~,nlx2),otherwise

Berikut adalah contoh _yang tidak diketahui, Xadalah ruang yang dikenal, Oadalah sel berwarna yang dikenal dan Lhilang nyawa

[2,2,4] 15                  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
(1) -> [2,2,4] 11           _ _ _ _ _ _ _ _ _ _ _ L X X X
    (1) -> [2,2,4] 7        _ _ _ _ _ _ _ L X X X L X X X
        0                   X X X L X X X L X X X L X X X
    (2) -> [2,2] 5          _ _ _ _ _ X O O O O L L X X X
        0                   O O X O O X O O O O L L X X X 
(2) -> [2,2] 9              _ _ _ _ _ _ _ _ _ X O O O O L
    (1) -> [2,2] 7          _ _ _ _ _ _ _ L X X O O O O L
        (1) -> [2,2] 5      _ _ _ _ _ L X L X X O O O O L
            0               O O X O O L X L X X O O O O L
        (2) -> [2] 3        _ _ _ X O O L L X X O O O O L
            1               O O L X O O L L X X O O O O L               
    (2) -> [2] 5            _ _ _ _ _ X O O L X O O O O L
        2                   O O L L X X O O L X O O O O L

O(2n)

Mari kita mendefinisikan fungsi biaya h

h(l,n)=n1xlix+1

h

h

h(l,n-lx)=n-lx-1xlsaya-x+1=(n-1xlsaya-x+1)-lx=h(l,n)-lx

h(l~,nlx2)=nlx21x1li(x1)+1=(n1xlix+1)1=h(l,n)1

h(l,n)={0,if 1xli+x1nnlx,if x=1max{h(l,nlx)+lxh(l~,nlx2)+1,otherwise

h pada setiap langkah untuk mendapatkan kasus terburuk, jadi mari kita periksa perbedaan antara dua ekspresi dalam perulangan

[h(l,n-lx)+lx]-[h(l~,n-lx-2)+1]=n-lx-n-1xlsaya-x+1+lx-[n-lx-2-1x-1lsaya-(x-1)+1+1]=-2

[h(l,nlx)+lx][h(l~,nlx2)+1]=2[h(l,nlx)+lx][h(l~,nlx2)+1]<0[h(l,nlx)+lx]<[h(l~,nlx2)+1]

h(l,n)={0,if 1xli+x1nnlx,if x=1h(l~,nlx2)+1otherwise

Finally this recursive definition of h shows us that option (2) in function f is always the worst case (giving the maximum number of possibilities i.e. maximizing h)

f(l,n)={0,if 1xli+x1nnlx1if x=11+f(l~,nlx2),otherwise

Every step we decrease n by at least 3 and there is now a single recursive call, complexity is O(n)

crashoz
sumber
2
Welcome to PPCG, incredible first post!
cole
1
@cole It's not their first post, but it surely is incredible! Very clever approach +1
Mr. Xcoder
1
Awesome work. I'll award the bounty 2 days later, if no one finds any serious logical flaw until then.
Bubbler
2

Python, 303 289 bytes

First golf in a long time, so there may be a lot of excess fat. (Thanks Jo King for finding 14 bytes-worth.)

Function f generates all the possible arrangements (though always with a blank as the first character, but that's fine as long as we increment the length by 1 before we call it). Function g picks the position with the fewest number of blanks and recurses. Function h puts them together.

f=lambda l,n:["."*i+"X"*l[0]+c for i in range(1,n-l[0]+1)for c in f(l[1:],n-i-l[0])]if l else["."*n]
def g(q,n):O,X=min([[[p[:i]+p[i+1:]for p in q if p[i]==u]for u in".X"]for i in range(n)],key=lambda x:len(x[0]));return(len(q)>1)*1and max(1+g(O,n-1),g(X,n-1))
h=lambda l,n:g(f(l,n+1),n+1)

The examples all run fine:

>>> h([3,7],15)
2
>>> h([3,4],15)
3
>>> h([1,1,1,2,1],15)
6
Uri Granta
sumber
1
Are you allowed to return False for 0? If so, you can change (len(q)>1)*1and to len(q)>1and. If you are not allowed to return False for0, then do that, but change g(f(l,n+1),n+1) to 1*g(f(l,n+1),n+1) and it will still save one byte
Zacharý
1
Even better: in the case False is not allowed for 0, instead of changing g(f(l,n+1),n+1) to 1*g(f(l,n+1),n+1), change it to +g(f(l,n+1),n+1)
Zacharý
2
Also, you don't need to count the h= in your byte count
Zacharý
1
288 byte .
Jonathan Frech