Peta Kepulauan (dan sungai)

20

pengantar

Selama berabad-abad, ada sungai tertentu yang tidak pernah dipetakan. Guild of Cartographers ingin membuat peta sungai, namun, mereka tidak pernah berhasil - karena beberapa alasan, semua kartografer yang mereka kirim untuk memetakan sungai telah dimakan oleh binatang buas di daerah tersebut. Diperlukan pendekatan yang berbeda.

Deskripsi Input

Area tersebut adalah grid sel panjang mdan lebar n. Sel di kiri bawah adalah 0,0, dan sel di kanan atas adalah m-1,n-1. mdan ndisediakan dalam input sebagai tuple m,n.

Dengan menggunakan teknik sounding geografis jarak jauh lokasi pulau di sekitar sungai telah diidentifikasi. Ukuran pulau (yaitu berapa banyak sel yang ditempati pulau) juga telah diidentifikasi tetapi bentuknya belum. Kami menyediakan informasi ini dalam sebuah tuple di s,x,ymana sukuran pulau, dan xdan yadalah posisi x dan y dari satu sel tertentu dari pulau itu. Setiap tuple dalam input dipisahkan oleh ruang, jadi di sini adalah contoh input:

7,7 2,0,0 2,3,1 2,6,1 2,4,3 2,2,4 8,0,6 1,2,6 3,4,6

Untuk mengilustrasikan lebih jelas, berikut adalah input pada grafik:

 y 6|8 1 3
   5|
   4|  2
   3|    2
   2|
   1|   2  2
   0|2  
     =======
     0123456
     x

Deskripsi Output

Keluarkan peta menggunakan karakter ASCII untuk mewakili bagian dari area. Setiap sel akan menjadi #(tanah) atau .(air). Peta harus mengikuti aturan-aturan ini:

  1. Definisi. Pulau adalah kelompok sel tanah yang berdekatan secara ortogonal yang seluruhnya dibatasi oleh sel-sel sungai dan / atau perbatasan daerah tersebut.
  2. Definisi. Sungai adalah kelompok sel air yang berdekatan secara ortogonal yang dibatasi seluruhnya oleh sel tanah dan / atau perbatasan daerah tersebut, dan tidak mengandung "danau" (2x2 area sel air).
  3. Memerintah . Peta harus memuat tepat satu sungai.
  4. Memerintah . Setiap sel bernomor dalam input harus merupakan bagian dari pulau yang mengandung ssel dengan tepat .
  5. Memerintah . Setiap pulau di peta harus berisi persis salah satu sel bernomor dalam input.
  6. Memerintah . Ada satu peta unik untuk setiap input.

Berikut adalah output dari contoh input:

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

Berikut ini adalah input dan output lain.

Memasukkan:

5,5 3,0,1 1,4,1 2,0,4 2,2,4 2,4,4

Keluaran:

#.#.#
#.#.#
.....
###.#
.....
Absinth
sumber
3
Catatan: ini adalah pertanyaan yang sama dengan pemecah masalah Nurikabe .
absinth
1
Bisakah kita mengambil input dalam format apa pun yang nyaman, atau haruskah kita tetap menggunakan yang ada dalam pertanyaan?
Erik the Outgolfer
1
ini juga masalah 4 dari kompetisi Dyalog 2012
ngn
@ ngn Sejak kapan "memposting hash kriptografis" ... biasa? (tapi saya kira itu diizinkan ketika tantangan secara eksplisit mengizinkannya)
user202729
1
inilah bookmarklet untuk puzzle-nurikabe.com - mengkonversi puzzle saat ini menjadi input yang valid untuk tantangan ini dan menunjukkannya dengan warna merah tepat di bawah papan:javascript:(_=>{var t=Game.nurikabe().task,m=t.length,n=t[0].length,s=[m,n];for(var i=0;i<m;i++)for(var j=0;j<n;j++)if(t[i][j]>=0)s+=' '+[t[i][j],i,j];puzzleContainerDiv.insertAdjacentHTML('beforeend','<hr><tt style=color:red>'+s+'</tt><hr>')})();void(0)
ngn

Jawaban:

10

C + PicoSAT , 2345 995 952 byte

#include<picosat.h>
#define f(i,a)for(i=a;i;i--)
#define g(a)picosat_add(E,a)
#define b calloc(z+1,sizeof z)
#define e(a,q)if(a)A[q]^A[p]?l[q]++||(j[++k]=q):s[q]||(i[q]=p,u(q));
z,F,v,k,n,h,p,q,r,C,*x,*A,*i,*l,*s,*j,*m;u(p){s[m[++n]=p]=1;e(p%F-1,p-1)e(p%F,p+1)e(p>F,p-F)e(p<=F*v-F,p+F)}t(){f(q,k)l[j[q]]=0;f(q,n)s[m[q]]=0;k=n=0;i[p]=-1;u(p);}main(){void*E=picosat_init();if(scanf("%d,%d",&F,&v)-2)abort();z=F*v;for(x=b;scanf("%d,%d,%d",&r,&p,&q)==3;g(p),g(0))x[p=F-p+q*F]=r;f(p,F*v-F)if(p%F)g(p),g(p+1),g(p+F),g(p+F+1),g(0);for(A=b,i=b,l=b,s=b,j=b,m=b;!C;){picosat_sat(E,C=h=-1);f(p,F*v)A[p]=picosat_deref(E,p)>0,i[p]=0;f(p,F*v)if(x[p])if(i[q=p]){for(g(-q);i[q]+1;)q=i[q],g(-q);g(C=0);}else if(t(),r=n-x[p]){f(q,r<0?k:n)g(r<0?j[q]:-m[q]);g(C=0);}f(p,F*v)if(!i[p])if(t(),A[p]){g(-++z);f(q,k)g(j[q]);g(C=0);f(q,n)g(-m[q]),g(z),g(0);}else{C&=h++;f(q,k)g(-j[q]);g(++z);g(++z);g(0);f(q,F*v)g(s[q]-z),g(q),g(0);}}f(p,F*v)putchar(A[p]?35:46),p%F-1||puts("");}

Cobalah online!

(Peringatan: tautan TIO ini adalah URL 30 kilobyte yang berisi salinan PicoSAT 965 yang diperkecil, jadi Anda mungkin tidak dapat memuatnya di beberapa peramban, tetapi memuat setidaknya di Firefox dan Chrome.)

Bagaimana itu bekerja

Kami menginisialisasi pemecah SAT dengan variabel untuk setiap sel (tanah atau air), dan hanya kendala berikut:

  1. Setiap sel bernomor adalah tanah.
  2. Setiap persegi panjang 2 × 2 memiliki setidaknya satu tanah.

Sisa kendala sulit untuk disandikan langsung ke SAT, jadi alih-alih kami menjalankan solver untuk mendapatkan model, menjalankan urutan pencarian pertama untuk menemukan wilayah yang terhubung dari model ini, dan menambahkan kendala tambahan sebagai berikut:

  1. Untuk setiap sel bernomor di wilayah darat yang terlalu besar, tambahkan batasan bahwa harus ada setidaknya satu sel air di antara sel-sel tanah saat ini di wilayah itu.
  2. Untuk setiap sel bernomor di wilayah darat yang terlalu kecil, tambahkan batasan bahwa harus ada setidaknya satu sel tanah di antara sel air saat ini yang berbatasan dengan wilayah itu.
  3. Untuk setiap sel bernomor di wilayah darat yang sama dengan sel bernomor lainnya, tambahkan batasan bahwa harus ada setidaknya satu sel air di sepanjang jalur sel tanah saat ini di antara mereka (ditemukan dengan berjalan dengan pointer induk yang tersisa dari pencarian pertama-kedalaman) ).
  4. Untuk setiap wilayah daratan termasuk tidak ada sel bernomor, tambahkan kendala itu juga
    • semua sel tanah saat ini harus berupa air, atau
    • setidaknya salah satu sel air saat ini yang berbatasan dengan wilayah itu haruslah tanah.
  5. Untuk setiap wilayah perairan, tambahkan kendala yang baik
    • semua sel air saat ini harusnya berupa tanah, atau
    • setiap sel selain dari sel air saat ini haruslah tanah, atau
    • setidaknya salah satu sel tanah saat ini yang berbatasan dengan wilayah itu haruslah air.

Mengambil keuntungan dari antarmuka tambahan ke pustaka PicoSAT, kita dapat segera menjalankan kembali solver termasuk kendala yang ditambahkan, menjaga semua kesimpulan sebelumnya yang dibuat oleh solver. PicoSAT memberi kami model baru, dan kami terus mengulangi langkah-langkah di atas sampai solusinya valid.

Ini sangat efektif; itu memecahkan 15x15 dan 20x20 contoh dalam sepersekian detik.

(Terima kasih kepada Lopsy karena menyarankan gagasan ini untuk secara interaktif membatasi daerah yang terhubung dalam pemecah SAT yang meningkat, beberapa waktu lalu.)

Beberapa test case yang lebih besar dari puzzle-nurikabe.com

Halaman acak berisi 15 × 15 teka-teki keras ( 5057541 , 5122197 , 5383030 , 6275294 , 6646970 , 6944232 ):

15,15 1,5,1 3,9,1 5,4,2 1,6,2 2,11,2 2,2,3 3,9,3 2,4,4 1,10,4 5,12,4 3,1,5 1,3,5 3,8,5 1,13,5 5,5,6 1,12,6 1,2,8 2,9,8 1,1,9 2,6,9 6,11,9 3,13,9 5,2,10 2,4,10 4,10,10 1,5,11 2,12,11 2,3,12 2,8,12 5,10,12 1,5,13 1,9,13 1,6,14 1,8,14
15,15 4,2,0 2,5,0 1,3,1 2,14,2 1,3,3 2,11,3 1,13,3 1,5,4 11,7,4 1,9,4 1,4,5 1,8,5 2,10,5 12,14,5 3,5,6 1,4,7 2,10,7 3,9,8 4,0,9 1,4,9 1,6,9 3,10,9 1,5,10 1,7,10 8,9,10 1,1,11 10,3,11 2,11,11 6,0,12 1,11,13 2,9,14 1,12,14
15,15 2,2,0 8,10,0 2,3,1 2,14,2 2,3,3 3,5,3 3,9,3 2,11,3 5,13,3 6,0,4 3,7,4 3,3,5 2,11,5 2,6,6 1,8,6 1,4,7 2,10,7 1,6,8 2,8,8 5,3,9 2,11,9 2,7,10 7,14,10 2,1,11 4,3,11 2,5,11 1,9,11 2,11,11 2,0,12 4,6,13 1,11,13 3,4,14 1,12,14
15,15 2,0,0 2,4,0 3,6,1 2,10,1 1,13,1 2,5,2 2,12,2 3,0,3 2,2,3 4,7,3 2,9,3 1,14,3 1,4,4 1,8,4 2,12,5 4,2,6 3,4,6 1,14,6 7,7,7 1,10,8 2,12,8 3,2,9 2,14,9 2,0,10 2,6,10 1,10,10 2,5,11 4,7,11 2,12,11 1,14,11 3,2,12 3,9,12 1,1,13 2,4,13 3,8,13 2,10,14 5,14,14
15,15 1,3,0 1,14,0 3,7,1 3,10,1 2,13,1 3,1,2 4,5,2 2,12,3 3,3,4 1,8,4 1,1,5 3,5,5 1,9,5 5,13,5 3,3,6 1,8,6 2,2,7 2,12,7 1,6,8 1,8,8 2,11,8 2,1,9 4,5,9 2,9,9 2,13,9 2,6,10 4,11,10 1,2,11 3,9,12 2,13,12 3,1,13 2,4,13 3,7,13 1,0,14
15,15 2,8,0 2,4,1 2,7,1 1,10,1 6,4,3 1,1,4 12,5,4 3,11,4 5,13,4 3,10,5 3,0,6 1,6,6 2,8,6 4,13,7 2,3,8 1,6,8 3,8,8 2,14,8 2,4,9 5,1,10 4,3,10 1,9,10 6,13,10 3,8,11 1,10,11 3,4,13 2,7,13 3,10,13 1,6,14 1,14,14

Halaman acak 20 × 20 puzzle normal ( 536628 , 3757659 ):

20,20 1,0,0 3,2,0 2,6,0 1,13,0 3,9,1 3,15,1 2,7,2 3,13,2 3,0,3 2,3,3 3,18,3 3,5,4 2,9,4 2,11,4 2,16,4 1,0,5 2,7,5 1,10,5 1,19,5 3,2,6 1,11,6 2,17,6 2,0,7 3,4,7 5,6,7 2,9,7 4,13,7 3,15,7 1,3,8 1,10,8 1,14,9 2,18,9 3,1,10 2,4,10 1,8,10 1,10,10 3,12,10 3,16,10 1,9,11 1,17,11 2,19,11 2,0,12 2,2,12 1,4,12 4,6,12 2,13,12 2,15,12 1,14,13 2,17,13 1,3,14 2,5,14 4,7,14 2,15,14 3,0,15 1,2,15 2,13,15 3,18,15 3,7,16 7,10,16 1,17,16 2,0,17 2,3,17 2,5,17 3,11,17 3,15,17 1,0,19 1,2,19 1,4,19 2,6,19 5,8,19 1,11,19 1,13,19 3,15,19 2,18,19
20,20 1,0,0 1,4,0 5,8,0 1,17,0 1,19,0 2,17,2 3,6,3 2,10,3 2,12,3 4,14,3 6,0,4 3,4,4 4,7,4 1,11,4 1,18,4 1,6,5 3,12,5 4,15,5 4,4,6 2,16,6 2,19,6 6,0,7 3,10,7 2,12,8 2,17,8 3,3,9 2,5,9 4,8,9 2,10,9 3,0,10 1,2,10 5,14,10 2,16,10 2,19,10 7,7,11 3,12,12 2,17,12 2,2,13 4,4,13 3,6,13 4,14,13 3,0,14 1,3,14 1,5,14 3,16,14 1,2,15 1,9,15 2,11,15 5,13,15 3,19,15 1,4,16 3,6,16 1,3,17 1,12,17 1,14,17 1,16,17 6,0,19 2,2,19 3,5,19 2,7,19 5,9,19 1,11,19 2,13,19 1,15,19 4,17,19
Anders Kaseorg
sumber
3

GLPK , (tidak bersaing) 2197 byte

Ini adalah entri yang tidak bersaing, seperti

  • Saya tidak mengikuti format data input (karena GLPK hanya dapat membaca input data dari file) dan
  • GLPK tampaknya tidak tersedia di RIO.

Saya akan menyimpan versi yang masih belum diubah, namun fungsional di sini. Bergantung pada umpan balik, saya mungkin mencoba mempersingkat + menambahkan penjelasan jika ada minat. Sejauh ini, nama kendala berfungsi sebagai dokumen di tempat.

Gagasan utamanya adalah untuk menyandikan batasan "pulau-pulau yang berdekatan" dengan memperkenalkan variabel aliran terpelihara yang memiliki sumber yang ditentukan sebelumnya di lokasi petunjuk. Variabel keputusan is_islandkemudian memainkan peran tempat tenggelam. Dengan meminimalkan jumlah total aliran ini, pulau-pulau tersebut dipaksa untuk tetap terhubung secara optimal. Kendala lain agak langsung menerapkan berbagai aturan. Apa yang membingungkan saya yang sepertinya masih saya butuhkanisland_fields_have_at_least_one_neighbor .

Kinerja formulasi ini tidak bagus. Dengan langsung memakan semua kompleksitas dengan sejumlah besar kendala, pemecah membutuhkan waktu hampir 15 detik untuk contoh 15 x 15.

Kode (masih belum diubah)

# SETS
param M > 0, integer; # length
param N > 0, integer; # width
param P > 0, integer; # no. of islands

set X := 0..N-1;  # set of x coords
set Y := 0..M-1;  # set of y coords
set Z := 0..P-1;  # set of islands

set V := X cross Y;
set E within V cross V := setof{
  (sx, sy) in V, (tx, ty) in V :

  ((abs(sx - tx) = 1) and (sy = ty)) or 
  ((sx = tx) and (abs(sy - ty) = 1))
} 
  (sx, sy, tx, ty);


# PARAMETERS
param islands{x in X, y in Y, z in Z}, integer, default 0;
param island_area{z in Z} := sum{x in X, y in Y} islands[x, y, z];

# VARIABLES
var is_river{x in X, y in Y}, binary;
var is_island{x in X, y in Y, z in Z}, binary;
var flow{(sx, sy, tx, ty) in E, z in Z} >= 0;

# OBJECTIVE
minimize obj: sum{(sx, sy, tx, ty) in E, z in Z} flow[sx, sy, tx, ty, z];

# CONSTRAINTS
s.t. islands_are_connected{(x, y) in V, z in Z}:

    islands[x, y, z] 
  - is_island[x, y, z]
  + sum{(sx, sy, tx, ty) in E: x = tx and y = ty} flow[sx, sy, x, y, z]
  - sum{(sx, sy, tx, ty) in E: x = sx and y = sy} flow[x, y, tx, ty, z]
  = 0;


s.t. island_contains_hint_location{(x, y) in V, z in Z}:

    is_island[x, y, z] >= if islands[x, y, z] > 0 then 1 else 0;


s.t. each_square_is_river_or_island{(x, y) in V}:

    is_river[x, y] + sum{z in Z} is_island[x, y, z] = 1;


s.t. islands_match_hint_area{z in Z}:

    sum{(x, y) in V} is_island[x, y, z] = island_area[z];


s.t. river_has_no_lakes{(x,y) in V: x > 0 and y > 0}:

  3 >= is_river[x, y] + is_river[x - 1, y - 1]
     + is_river[x - 1, y] + is_river[x, y - 1];


s.t. river_squares_have_at_least_one_neighbor{(x, y) in V}:

    sum{(sx, sy, tx, ty) in E: x = tx and y = ty} is_river[sx, sy] 
 >= is_river[x, y];


s.t. island_fields_have_at_least_one_neighbor{(x, y) in V, z in Z: island_area[z] > 1}:

    sum{(sx, sy, tx, ty) in E: x = tx and y = ty} is_island[sx, sy, z]
 >= is_island[x, y, z];


s.t. islands_are_separated_by_water{(x, y) in V, z in Z}:

    sum{(sx, sy, tx, ty) in E, oz in Z: x = sx and y = sy and z != oz} is_island[tx, ty, oz]
 <= 4 * P * (1 - is_island[x, y, z]);

solve;

for{y in M-1..0 by -1}
{
    for {x in X} 
    {
        printf "%s", if is_river[x, y] = 1 then "." else "#";
    }
    printf "\n";
}

Data masalah

5 x 5

data;
param M := 5;
param N := 5;
param P := 5;
param islands :=
# x,y,z,area
  0,1,0,3
  4,1,1,1
  0,4,2,2
  2,4,3,2
  4,4,4,2;
end;

7 x 7

data;
param M := 7;
param N := 7;
param P := 8;
param islands :=
# x,y,z,area
  0,0,0,2 
  3,1,1,2 
  6,1,2,2 
  4,3,3,2 
  2,4,4,2 
  0,6,5,8 
  2,6,6,1 
  4,6,7,3;
end;

15 x 15

data;
param M := 15;
param N := 15;
param P := 34;
param islands :=
# x,y,   z,area
5,  1,   0, 1
9,  1,   1, 3
4,  2,   2, 5
6,  2,   3, 1
11, 2,   4, 2
2,  3,   5, 2
9,  3,   6, 3
4,  4,   7, 2
10, 4,   8, 1
12, 4,   9, 5
1,  5,  10, 3
3,  5,  11, 1
8,  5,  12, 3
13, 5,  13, 1
5,  6,  14, 5
12, 6,  15, 1
2,  8,  16, 1
9,  8,  17, 2
1,  9,  18, 1
6,  9,  19, 2
11, 9,  20, 6
13, 9,  21, 3
2,  10, 22, 5
4,  10, 23, 2
10, 10, 24, 4
5,  11, 25, 1
12, 11, 26, 2
3,  12, 27, 2
8,  12, 28, 2
10, 12, 29, 5
5,  13, 30, 1
9,  13, 31, 1
6,  14, 32, 1
8,  14  33, 1;
end;

Pemakaian

Telah glpsolterpasang, model sebagai satu file (mis. river.mod), Memasukkan data dalam file terpisah (mis 7x7.mod). Kemudian:

glpsol -m river.mod -d 7x7.mod

Ini ditambah beberapa kesabaran akan mencetak solusi dalam format output yang ditentukan (bersama dengan output diagnostik "beberapa").

ojdo
sumber
2
Saya pikir jawaban ini harus dianggap bersaing karena mungkin bagi orang lain untuk memverifikasi bahwa itu berhasil. Perbedaan dalam format IO harus dicakup oleh asumsi bahwa format IO yang masuk akal harus diterima.
fəˈnɛtɪk
2
@ fəˈnɛtɪk Setuju. Itu tidak memenuhi syarat untuk hadiah ngn yang baru saja berakhir, yang secara khusus membutuhkan jawaban yang bisa dijalankan di TIO, tapi itu bukan persyaratan dari pertanyaan itu sendiri.
Anders Kaseorg
Mengingat bahwa saya belum mulai bermain golf, saya tidak akan menganggapnya bersaing (belum). Setelah saya yakin bahwa saya telah memangkas semua kendala yang berlebihan, saya akan menghapus semua pernyataan.
ojdo
3

Python 3 , 1295 byte

Ini adalah solusi hanya python. Tidak menggunakan pustaka dan memuat papan melalui input standar. Penjelasan lebih lanjut akan datang. Ini adalah upaya pertama saya di golf yang begitu besar. Ada tautan ke kode yang dikomentari dan tidak ada golf di bagian bawah.

L,S,O,R,F=len,set,None,range,frozenset
U,N,J,D,I=S.update,F.union,F.isdisjoint,F.difference,F.intersection
def r(n,a,c):
 U(c,P)
 if L(I(N(Q[n],C[n]),a))<2:return 1
 w=D(P,N(a,[n]));e=S();u=S([next(iter(w))])
 while u:n=I(Q[u.pop()],w);U(u,D(n,e));U(e,n)
 return L(e)==L(w)
def T(a,o,i,c,k):
 s,p,m=a
 for _ in o:
  t=s,p,N(m,[_]);e=D(o,[_])
  if t[2] in c:o=e;continue
  c.add(t[2]);n=D(Q[_],m);U(k,n)
  if not J(i,n)or not r(_,N(m,i),k):o=e
  elif s==L(t[2]):yield t
  else:yield from T(t,N(e,n),i,c,k)
s,*p=input().split()
X,Y=eval(s)
A=[]
l=1,-1,0,0
P=F((x,y)for y in R(Y)for x in R(X))
exec("Q%sl,l[::-1]%s;C%s(1,1,-1,-1),l[:2]*2%s"%(('={(x,y):F((x+i,y+j)for i,j in zip(',')if X>x+i>-1<y+j<Y)for x,y in P}')*2))
for a in p:a,x,y=eval(a);k=x,y;A+=[(a,k,F([k]))]
A.sort(reverse=1)
k=F(a[1]for a in A)
p=[O]*L([a for a in A if a[0]!=1])
g,h=p[:],p[:]
i=0
while 1:
 if g[i]is O:h[i]=S();f=O;g[i]=T(A[i],Q[A[i][1]],D(N(k,*p[:i]),[A[i][1]]),S(),h[i])
 try:p[i]=g[i].send(f)[2]
 except:
  f=I(N(k,*p[:i]),h[i]);g[i]=p[i]=O;i-=1
  while J(p[i],f):g[i]=p[i]=O;i-=1
 else:
  i+=1
  if i==L(p):
   z=N(k,*p)
   if not any(J(z,F(zip([x,x+1]*2,[y,y,y+1,y+1])))for x in R(X-1)for y in R(Y-1)):break
   for c in h:U(c,z)
b=[X*['.']for i in R(Y)]
for x,y in z:b[y][x]='#'
for l in b[::-1]:print(''.join(l))

Cobalah online!

Lihatlah kode un-golfed .

Matt
sumber