Mappa delle isole (e un fiume)


20

introduzione

Per molti secoli, c'è stato un certo fiume che non è mai stato mappato. La Gilda dei Cartografi vuole produrre una mappa del fiume, tuttavia, non è mai riuscita ad avere successo - per qualche ragione, tutti i cartografi che hanno inviato per mappare il fiume sono stati mangiati da animali selvatici nella zona. È richiesto un approccio diverso.

Descrizione dell'input

L'area è una griglia rettangolare di celle di lunghezza me larghezza n. La cella in basso a sinistra sarebbe 0,0, e la cella in alto a destra sarebbe m-1,n-1. me nsono forniti nell'input come tupla m,n.

Utilizzando tecniche di sondaggio geografico a lunga distanza è stata identificata la posizione delle isole intorno al fiume. Sono state identificate anche le dimensioni delle isole (cioè quante celle occupa l'isola) ma non la forma. Forniamo queste informazioni in una tupla s,x,ydove ssono le dimensioni dell'isola xe ysono le posizioni xey di una cella particolare di quell'isola. Ogni tupla nell'input è separata dallo spazio, quindi ecco un input di esempio:

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

Per illustrare più chiaramente, ecco l'input su un grafico:

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

Descrizione dell'uscita

Stampa una mappa usando caratteri ASCII per rappresentare parti dell'area. Ogni cella sarà #(terra) o .(acqua). La mappa dovrebbe seguire queste regole:

  1. Definizione. Un'isola è un gruppo ortogonalmente contiguo di cellule terrestri che è delimitato interamente da celle fluviali e / o dal confine dell'area.
  2. Definizione. Un fiume è un gruppo ortogonalmente contiguo di celle d'acqua che è delimitato interamente da cellule terrestri e / o dal confine dell'area e non contiene "laghi" (2x2 aree di celle d'acqua).
  3. Regola . La mappa deve contenere esattamente un fiume.
  4. Regola . Ogni cella numerata nell'input deve far parte di un'isola contenente esattamente scelle.
  5. Regola . Ogni isola nella mappa deve contenere esattamente una delle celle numerate nell'input.
  6. Regola . Esiste un'unica mappa unica per ogni input.

Ecco l'output dell'input di esempio:

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

Ecco un altro input e output.

Ingresso:

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

Produzione:

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

3
Nota: questa è la stessa domanda di un solutore Nurikabe .
assenzio

1
Possiamo prendere input in qualsiasi formato conveniente o dovremmo attenerci a quello nella domanda?
Erik the Outgolfer,

1
questo è anche il problema 4 della competizione Dyalog 2012
ngn

@ngn Da quando è "pubblica un hash crittografico" ... al solito? (ma suppongo sia consentito quando una sfida lo consente esplicitamente)
user202729

1
ecco un bookmarklet per puzzle-nurikabe.com - converte il puzzle corrente in un input valido per questa sfida e lo mostra in rosso proprio sotto il tabellone: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

Risposte:


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("");}

Provalo online!

(Attenzione: questo link TIO è un URL da 30 kilobyte che contiene una copia minimizzata di PicoSAT 965, quindi potresti non essere in grado di caricarlo in alcuni browser, ma si carica almeno in Firefox e Chrome.)

Come funziona

Inizializziamo il solutore SAT con una variabile per ogni cella (terra o acqua) e solo i seguenti vincoli:

  1. Ogni cella numerata è terra.
  2. Ogni rettangolo 2 × 2 ha almeno una terra.

Il resto dei vincoli è difficile da codificare direttamente in SAT, quindi invece eseguiamo il solutore per ottenere un modello, eseguiamo una sequenza di ricerche approfondite per trovare le regioni connesse di questo modello e aggiungere ulteriori vincoli come segue:

  1. Per ogni cella numerata in una regione di terra che è troppo grande, aggiungi un vincolo che dovrebbe esserci almeno una cella di acqua tra le attuali celle di terra in quella regione.
  2. Per ogni cella numerata in una regione di terra che è troppo piccola, aggiungi un vincolo che ci dovrebbe essere almeno una cellula di terra tra le attuali celle d'acqua che confinano con quella regione.
  3. Per ogni cella numerata nella stessa regione di terra di un'altra cella numerata, aggiungi un vincolo che dovrebbe esserci almeno una cella d'acqua lungo il percorso delle celle di terra correnti tra di loro (trovata camminando sui puntatori padre lasciati dalla prima ricerca di profondità ).
  4. Per ogni regione di terra senza celle numerate, aggiungi anche i vincoli
    • tutte quelle attuali celle terrestri dovrebbero essere acqua, o
    • almeno una delle attuali celle d'acqua che confina con quella regione dovrebbe essere terra.
  5. Per ogni regione acquatica, aggiungi anche dei vincoli
    • tutte quelle attuali celle ad acqua dovrebbero essere terra, o
    • ogni cellula diversa da quelle attuali dovrebbe essere terra o
    • almeno una delle attuali celle terrestri al confine con quella regione dovrebbe essere acqua.

Sfruttando l'interfaccia incrementale della libreria PicoSAT, possiamo immediatamente rieseguire il risolutore inclusi i vincoli aggiunti, preservando tutte le precedenti inferenze fatte dal risolutore. PicoSAT ci offre un nuovo modello e continuiamo a ripetere i passaggi precedenti fino a quando la soluzione non è valida.

Questo è straordinariamente efficace; risolve istanze 15 × 15 e 20 × 20 in una piccola frazione di secondo.

(Grazie a Lopsy per aver suggerito questa idea di vincolare in modo interattivo le regioni connesse in un solutore SAT incrementale, qualche tempo fa.)

Alcuni casi di test più grandi di puzzle-nurikabe.com

Una pagina casuale di 15 × 15 puzzle ( 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

Una pagina casuale di 20 × 20 puzzle normali ( 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

3

GLPK , 2197 byte (non concorrenti)

Questa è una voce non competitiva, come

  • Non seguo il formato dei dati di input (poiché GLPK può solo leggere i dati di input dai file) e
  • GLPK sembra non essere disponibile su RIO.

Salverò qui una versione ancora non giocata, ma funzionale. A seconda del feedback, potrei provare ad accorciarlo + aggiungere una spiegazione se c'è interesse. Finora, i nomi dei vincoli servono come documenti sul posto.

L'idea principale è quella di codificare il vincolo "isole contigue" introducendo una variabile di flusso preservata che ha una fonte predefinita nella posizione del suggerimento. La variabile decisionale is_islandsvolge quindi il ruolo di lavandini posizionabili. Riducendo al minimo la somma totale di questo flusso, le isole sono costrette a rimanere collegate in modo ottimale. Gli altri vincoli invece implementano direttamente le varie regole. Cosa mi confonde di cui mi sembra ancora di aver bisogno island_fields_have_at_least_one_neighbor.

Tuttavia, le prestazioni di questa formulazione non sono eccezionali. Mangiando direttamente tutta la complessità con una grande quantità di vincoli, il solutore impiega quasi 15 secondi per l'esempio 15 x 15.

Codice (ancora non salvato)

# 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";
}

Dati problematici

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;

uso

Hanno glpsolinstallato, modello come un unico file (ad es. river.mod), Input di dati in file separati (es 7x7.mod.). Poi:

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

Questo oltre a un po 'di pazienza stamperà la soluzione nel formato di output specificato (insieme a "alcuni" output diagnostici).


2
Penso che questa risposta debba essere considerata competitiva in quanto è possibile per altre persone verificare che funzioni. Le differenze nel formato I / O dovrebbero essere coperte dal presupposto che qualsiasi formato I / O ragionevole dovrebbe essere accettato.
fəˈnɛtɪk,

2
@ fəˈnɛtɪk Concordato. Non era ammissibile per la generosità di Ngn che era appena terminata, che richiedeva specificamente una risposta eseguibile su TIO, ma questo non è un requisito della domanda stessa.
Anders Kaseorg,

Dato che non ho iniziato a giocare a golf, non lo considero competitivo (ancora). Una volta che sono sicuro di aver eliminato tutti i vincoli ridondanti, eseguirò un carboncino di tutte le dichiarazioni.
ojdo,

3

Python 3 , 1295 byte

Questa è una soluzione solo per Python. Non utilizza librerie e carica la scheda tramite input standard. Ulteriori spiegazioni a venire. Questo è il mio primo tentativo di golf così grande. C'è un link al codice commentato e non giocato a golf in fondo.

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

Provalo online!

Guarda il codice non golf .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.