Карта островов (и река)

20

Вступление

На протяжении многих веков существовала определенная река, которая никогда не была нанесена на карту. Гильдия картографов хочет составить карту реки, однако, им так и не удалось добиться успеха - по какой-то причине все картографы, которых они отправили на карту реки, были съедены дикими животными в этом районе. Требуется другой подход.

Описание входа

Область представляет собой прямоугольную сетку ячеек длины mи ширины n. Ячейка в левом нижнем углу будет 0,0, и ячейка в верхнем правом углу будет m-1,n-1. mи nпредоставляются на входе в виде кортежа m,n.

С использованием методов географического зондирования на больших расстояниях было выявлено расположение островков вокруг реки. Размер островков (то есть, сколько клеток занимает остров) также были определены, но форма не имеет. Мы эту информацию в кортеже , s,x,yгде sэто размер острова, а также xи yявляются х и у позиции одной конкретной ячейки этого острова. Каждый кортеж на входе разделен пробелами, поэтому вот пример ввода:

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

Чтобы проиллюстрировать более наглядно, вот входные данные на графике:

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

Описание выхода

Выведите карту, используя символы ASCII для представления частей области. Каждая клетка будет либо #(земля), либо .(вода). Карта должна соответствовать следующим правилам:

  1. Определение. Остров - это ортогонально смежная группа сухопутных ячеек, полностью ограниченная речными ячейками и / или границей района.
  2. Определение. Река - это ортогонально смежная группа водных клеток, которая полностью ограничена сухопутными клетками и / или границей области и не содержит "озер" (2x2 площади водных клеток).
  3. Правило . Карта должна содержать ровно одну реку.
  4. Правило . Каждая пронумерованная ячейка на входе должна быть частью острова, содержащего именно sячейки.
  5. Правило . Каждый остров на карте должен содержать ровно одну из пронумерованных ячеек на входе.
  6. Правило . Существует одна уникальная карта для каждого входа.

Вот вывод примера ввода:

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

Вот еще один вход и выход.

Входные данные:

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

Выход:

#.#.#
#.#.#
.....
###.#
.....
абсент
источник
3
Примечание: это тот же вопрос, что и у решателя Nurikabe .
Абсент
1
Можем ли мы принять ввод в любом удобном формате, или мы должны придерживаться того вопроса?
Эрик Outgolfer
1
это также проблема 4 из соревнования
Dyalog
@ngn С каких это пор "размещать криптографический хеш" ... обычно? (но я предполагаю, что это разрешено, когда вызов явно разрешает это)
user202729
1
вот букмарклет для головоломки-nurikabe.com - он преобразует текущую головоломку в действительный вход для этого 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)
задания

Ответы:

10

C + PicoSAT , 2345 995 952 байта

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

Попробуйте онлайн!

(Предупреждение: эта ссылка TIO представляет собой URL-адрес размером 30 килобайт, содержащий минимизированную копию PicoSAT 965, поэтому вы не сможете загрузить ее в некоторых браузерах, но она загружается как минимум в Firefox и Chrome.)

Как это устроено

Мы инициализируем решатель SAT с переменной для каждой ячейки (земли или воды) и только со следующими ограничениями:

  1. Каждая пронумерованная клетка - это земля.
  2. Каждый прямоугольник 2 × 2 имеет хотя бы одну землю.

Остальные ограничения сложно кодировать непосредственно в SAT, поэтому вместо этого мы запускаем решатель, чтобы получить модель, запускаем последовательность операций поиска в глубину, чтобы найти связанные области этой модели, и добавляем дополнительные ограничения следующим образом:

  1. Для каждой пронумерованной ячейки в области земли, которая слишком велика, добавьте ограничение на то, что среди текущих ячеек земли в этой области должна быть хотя бы одна водяная ячейка.
  2. Для каждой пронумерованной ячейки в области земли, которая слишком мала, добавьте ограничение, согласно которому должна быть хотя бы одна наземная ячейка среди текущих водных элементов, граничащих с этой областью.
  3. Для каждой пронумерованной ячейки в той же области суши, что и другая пронумерованная ячейка, добавьте ограничение, согласно которому должна быть хотя бы одна водяная ячейка на пути между текущими ячейками суши между ними (определяется путем обхода родительских указателей, оставшихся после поиска в глубину). ).
  4. Для каждого региона, включая ячейки без номеров, добавьте ограничения, которые либо
    • все эти современные наземные клетки должны быть водой, или
    • по крайней мере одна из нынешних водоемов, граничащих с этим регионом, должна быть сушей.
  5. Для каждого водного региона добавьте ограничения, которые либо
    • все эти нынешние водные ячейки должны быть наземными или
    • каждая клетка, кроме этих нынешних водяных ячеек, должна быть наземной, или
    • по крайней мере одна из нынешних сухопутных ячеек, граничащих с этим регионом, должна быть водой.

Используя преимущества инкрементного интерфейса к библиотеке PicoSAT, мы можем немедленно перезапустить решатель, включая добавленные ограничения, сохранив все предыдущие выводы, сделанные решателем. PicoSAT дает нам новую модель, и мы продолжаем повторять описанные выше шаги, пока решение не станет действительным.

Это удивительно эффективно; он решает 15 × 15 и 20 × 20 экземпляров за доли секунды.

(Спасибо Лопси за то, что он предложил эту идею интерактивного ограничения связанных областей в инкрементном SAT-решателе, некоторое время назад.)

Несколько больших тестовых примеров из головоломки-nurikabe.com

Случайная страница 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

Случайная страница с 20 × 20 нормальными головоломками ( 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 байт

Это неконкурентная запись, так как

  • Я не слежу за форматом входных данных (поскольку GLPK может только читать входные данные из файлов) и
  • GLPK, кажется, не доступен на RIO.

Я сохраню здесь еще не функциональную, но функциональную версию. В зависимости от обратной связи, я мог бы попытаться сократить это + добавить объяснение, если есть интерес. До сих пор имена ограничений служат документами на месте.

Основная идея заключается в кодировании ограничения «смежных островов» путем введения сохраненной переменной потока, которая имеет заранее заданный источник в месте подсказки. Решающая переменная is_islandтогда играет роль размещаемых поглотителей. Минимизируя общую сумму этого потока, острова вынуждены оставаться соединенными в оптимуме. Другие ограничения довольно непосредственно реализуют различные правила. Что меня озадачивает, что мне все еще нужно island_fields_have_at_least_one_neighbor.

Производительность этой формулировки не велика, хотя. При непосредственном поглощении всей сложности с большим количеством ограничений, решателю требуется около 15 секунд для примера 15 x 15.

Код (до сих пор не разгромлен)

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

Проблемные данные

5 х 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 х 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 х 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;

использование

Были glpsolустановлены, модель в виде одного файла (например river.mod), входные данные в отдельный файл (ы) (например 7x7.mod). Потом:

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

Это плюс некоторое терпение распечатает решение в указанном формате вывода (вместе с «некоторым» выводом диагностики).

ojdo
источник
2
Я думаю, что этот ответ следует считать конкурирующим, поскольку другие могут проверить, что он работает. Различия в формате ввода-вывода должны охватываться допущением, что любой приемлемый формат ввода-вывода должен быть принят.
fəˈnɛtɪk
2
@ fəˈnɛtɪk Согласен. Он не имел права на награду ngn, которая только что закончилась, что, в частности, требовало ответа, доступного для TIO, но это не требование самого вопроса.
Андерс Касеорг
Учитывая то, что я еще не начал играть в гольф, я не буду считать это соревнованием (пока). Как только я буду уверен, что я удалил все избыточные ограничения, я буду объявлять все объявления одним символом.
Ojdo
3

Python 3 , 1295 байт

Это решение только для Python. Он не использует библиотеки и загружает плату через стандартный ввод. Дальнейшее объяснение впереди. Это моя первая попытка в таком большом гольфе. Внизу есть ссылка на закомментированный код без кода.

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

Попробуйте онлайн!

Посмотрите на код без гольфа .

Мэтт
источник