Mapa das ilhas (e um rio)

20

Introdução

Por muitos séculos, houve um determinado rio que nunca foi mapeado. A Guilda dos Cartógrafos quer produzir um mapa do rio, no entanto, eles nunca conseguiram ter sucesso - por alguma razão, todos os cartógrafos que enviaram para mapear o rio foram comidos por animais selvagens na área. É necessária uma abordagem diferente.

Descrição da entrada

A área é uma grade retangular de células de comprimento me largura n. A célula no canto inferior esquerdo seria 0,0e a célula no canto superior direito seria m-1,n-1. me nsão fornecidos na entrada como uma tupla m,n.

Usando técnicas de som geográfico de longa distância, a localização das ilhas ao redor do rio foi identificada. O tamanho das ilhas (ou seja, quantas células a ilha ocupa) também foi identificado, mas a forma não. Nós fornecemos essas informações em uma tupla s,x,yonde sestá o tamanho da ilha xe ysão as posições x e y de uma célula específica dessa ilha. Cada tupla na entrada é separada por espaço, então aqui está um exemplo de entrada:

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

Para ilustrar mais claramente, aqui está a entrada em um gráfico:

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

Descrição da saída

Saída de um mapa usando caracteres ASCII para representar partes da área. Cada célula será #(terra) ou .(água). O mapa deve seguir estas regras:

  1. Definição. Uma ilha é um grupo ortogonalmente contíguo de células terrestres que é delimitado inteiramente por células do rio e / ou pela borda da área.
  2. Definição. Um rio é um grupo ortogonalmente contíguo de células de água que é delimitado inteiramente por células terrestres e / ou pela borda da área e não contém "lagos" (áreas 2x2 de células de água).
  3. Regra . O mapa deve conter exatamente um rio.
  4. Regra . Cada célula numerada na entrada deve fazer parte de uma ilha contendo exatamente scélulas.
  5. Regra . Cada ilha no mapa deve conter exatamente uma das células numeradas na entrada.
  6. Regra . Existe um único mapa exclusivo para cada entrada.

Aqui está a saída da entrada de exemplo:

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

Aqui está outra entrada e saída.

Entrada:

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

Saída:

#.#.#
#.#.#
.....
###.#
.....
absinto
fonte
3
Nota: esta é a mesma pergunta que um solucionador de Nurikabe .
absinthe
1
Podemos receber sugestões em qualquer formato conveniente ou devemos nos ater à que está em questão?
Erik the Outgolfer
1
este também é o problema 4 da competição Dyalog 2012
ngn
@ngn Desde quando é "postar um hash criptográfico" ... usual? (mas eu suponho que é permitido quando um desafio permitir explicitamente)
user202729
1
aqui está um bookmarklet para puzzle-nurikabe.com - ele converte o enigma atual para uma entrada válida para este desafio e mostra em vermelho logo abaixo da placa: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

Respostas:

10

C + PicoSAT , 2345 995 952 bytes

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

Experimente online!

(Aviso: esse link do TIO é um URL de 30 kilobytes que contém uma cópia reduzida do PicoSAT 965, portanto, talvez você não consiga carregá-lo em alguns navegadores, mas carregue pelo menos no Firefox e no Chrome.)

Como funciona

Inicializamos o solucionador SAT com uma variável para cada célula (terra ou água) e apenas as seguintes restrições:

  1. Cada célula numerada é terra.
  2. Cada retângulo 2 × 2 possui pelo menos um terreno.

O restante das restrições é difícil de codificar diretamente no SAT; portanto, executamos o solucionador para obter um modelo, executamos uma sequência de pesquisas profundas para encontrar as regiões conectadas desse modelo e adicionamos restrições adicionais, como a seguir:

  1. Para cada célula numerada em uma região terrestre muito grande, adicione uma restrição de que deve haver pelo menos uma célula aquática entre as células terrestres atuais nessa região.
  2. Para cada célula numerada em uma região terrestre muito pequena, adicione uma restrição de que deve haver pelo menos uma célula terrestre entre as células de água atuais que fazem fronteira com essa região.
  3. Para cada célula numerada na mesma região terrestre que outra célula numerada, adicione uma restrição de que deve haver pelo menos uma célula aquática ao longo do caminho das células terrestres atuais entre elas (encontrada ao percorrer os ponteiros pai restantes da pesquisa inicial profunda )
  4. Para cada região terrestre, incluindo células não numeradas, adicione restrições que
    • todas essas células terrestres atuais devem ser água ou
    • pelo menos uma das células de água atuais que fazem fronteira com essa região deve ser terrestre.
  5. Para cada região de água, adicione restrições que
    • todas essas células de água atuais devem ser terrestres ou
    • todas as células que não sejam as células aquáticas atuais devem estar em terra ou
    • pelo menos uma das células terrestres atuais que fazem fronteira com essa região deve ser água.

Aproveitando a interface incremental da biblioteca PicoSAT, podemos executar novamente o solucionador imediatamente, incluindo as restrições adicionadas, preservando todas as inferências anteriores feitas pelo solucionador. O PicoSAT nos fornece um novo modelo e continuamos repetindo as etapas acima até que a solução seja válida.

Isso é notavelmente eficaz; resolve instâncias 15 × 15 e 20 × 20 em uma pequena fração de segundo.

(Agradecemos a Lopsy por sugerir essa ideia de restringir interativamente as regiões conectadas em um solucionador incremental de SAT, há um tempo.)

Alguns casos de teste maiores do puzzle-nurikabe.com

Uma página aleatória de quebra-cabeças rígidos 15 × 15 ( 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

Uma página aleatória de 20 × 20 quebra-cabeças normais ( 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
fonte
3

GLPK , (não concorrente) 2197 bytes

Esta é uma entrada não concorrente, pois

  • Não sigo o formato dos dados de entrada (como o GLPK só pode ler dados de entrada dos arquivos) e
  • O GLPK parece não estar disponível no RIO.

Vou salvar uma versão ainda não-gasta e funcional aqui. Dependendo do feedback, posso tentar reduzi-lo + adicionar uma explicação, se houver interesse. Até o momento, os nomes das restrições servem como documentos no local.

A idéia principal é codificar a restrição "ilhas contíguas", introduzindo uma variável de fluxo preservada que possui uma fonte pré-especificada no local da dica. A variável de decisão, em is_islandseguida, desempenha o papel de pias posicionáveis. Ao minimizar a soma total desse fluxo, as ilhas são forçadas a permanecer conectadas no ideal. As outras restrições implementam diretamente as várias regras. O que me intriga e que ainda pareço precisar island_fields_have_at_least_one_neighbor.

O desempenho desta formulação não é ótimo, no entanto. Ao comer diretamente toda a complexidade com uma grande quantidade de restrições, o solucionador leva quase 15 segundos para o exemplo 15 x 15.

Código (ainda não destruído)

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

Dados do problema

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

glpsolinstalado, modelo como um arquivo (por exemplo river.mod), os dados de entrada em arquivo separado (s) (por exemplo 7x7.mod). Então:

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

Isso, mais alguma paciência, imprimirá a solução no formato de saída especificado (junto com "alguma" saída de diagnóstico).

ojdo
fonte
2
Penso que esta resposta deve ser considerada concorrente, pois é possível que outras pessoas verifiquem se ela funciona. As diferenças no formato de IO devem ser cobertas com a suposição de que qualquer formato razoável de IO deve ser aceito.
13138
2
@ fəˈnɛtɪk Concordou. Não era elegível para a recompensa da ngn que acabou de terminar, o que exigia especificamente uma resposta executável no TIO, mas isso não é um requisito da pergunta em si.
Anders Kaseorg 13/07/19
Dado que ainda não comecei a jogar golfe, não o considerarei competitivo (ainda). Depois de ter certeza de que removi todas as restrições redundantes, utilizarei apenas todas as declarações.
Ojdo
3

Python 3 , 1295 bytes

Esta é uma solução apenas para python. Ele não usa bibliotecas e carrega a placa através da entrada padrão. Mais explicações por vir. Esta é a minha primeira tentativa de um golfe tão grande. Há um link para o código comentado e sem golfe na parte inferior.

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

Experimente online!

Veja o código não golfe .

The Matt
fonte