섬 (및 강)의지도


20

소개

여러 세기 동안, 결코 매핑되지 않은 특정 강이있었습니다. 지도 제작자 길드 (Guil of Cartographers)는 강지도를 만들고 싶어하지만 성공하지 못했습니다. 다른 접근 방식이 필요합니다.

입력 설명

이 영역은 길이 m와 너비 가 셀의 사각형 격자입니다 n. 왼쪽 하단 0,0의 셀은이고 오른쪽 상단의 셀은입니다 m-1,n-1. mn튜플의 입력에 제공된다 m,n.

장거리 지리적 사운 딩 기술을 사용하여 강 주변 섬의 위치가 식별되었습니다. 섬의 크기 (즉, 섬이 얼마나 많은 세포를 점유하는지)도 식별되었지만 모양은 그렇지 않습니다. 우리는 튜플이 정보를 제공 섬의 크기이고, 및 x와 해당 섬의 특정 셀의 y 위치이다. 입력의 각 튜플은 공백으로 구분되므로 입력 예는 다음과 같습니다.s,x,ysxy

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. 정의. 강 토지 세포 및 / 또는 영역의 테두리로 완전히 묶여있다, 물 세포의 직교 연속 그룹 "호수"(물 세포의 2 × 2 영역)가 포함되어 있지 않습니다.
  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 Erik

1
이것은 또한 2012 년 Dyalog 공모전의
ngn

@ngn "암호화 해시 게시"는 언제부터 시작됩니까? 평소? (하지만 도전이 명시 적으로 허용하면 허용된다고 가정합니다)
user202729

1
다음은 puzzle-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)
ngn

답변:


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 링크는 축소 된 PicoSAT 965 사본을 포함하는 30 킬로바이트 URL이므로 일부 브라우저에서는로드 할 수 없지만 최소한 Firefox 및 Chrome에서는로드됩니다.)

작동 원리

각 셀 (토지 또는 물)에 대한 변수를 사용하여 SAT 솔버를 초기화하고 다음 제한 사항 만 적용합니다.

  1. 번호가 매겨진 모든 세포는 땅입니다.
  2. 모든 2 × 2 사각형에는 하나 이상의 랜드가 있습니다.

나머지 제약 조건은 SAT로 직접 인코딩하기가 어렵 기 때문에 대신 솔버를 실행하여 모델을 얻고 깊이 우선 검색 시퀀스를 실행하여이 모델의 연결된 영역을 찾은 후 다음과 같이 제약 조건을 추가합니다.

  1. 너무 큰 육지 지역의 번호가 매겨진 모든 셀에 대해 해당 지역의 현재 육상 셀 중 적어도 하나의 워터 셀이 있어야한다는 제약 조건을 추가하십시오.
  2. 너무 작은 육지 지역의 번호가 매겨진 모든 셀에 대해 해당 지역을 경계로하는 현재 물 세포 중 적어도 하나의 육상 셀이 있어야한다는 제약 조건을 추가하십시오.
  3. 다른 번호가 매겨진 셀과 같은 육상 지역에있는 모든 번호가 매겨진 셀에 대해, 현재의 셀 사이의 경로를 따라 적어도 하나의 워터 셀이 있어야한다는 제약 조건을 추가하십시오 (깊이 우선 검색에서 남은 부모 포인터를 걸어서 찾음) ).
  4. 번호가 매겨진 셀을 포함하지 않는 모든 육지 지역에 대해
    • 현재의 모든 육상 세포는 물이거나
    • 해당 지역과 접하는 현재의 물 세포 중 적어도 하나는 육지 여야합니다.
  5. 모든 수역에 대해 다음 중 하나를 수행하십시오.
    • 현재의 물 세포는 모두 육지이거나
    • 현재의 물 세포 이외의 모든 세포는 육지이거나
    • 해당 지역과 접하는 현재의 육상 세포 중 적어도 하나는 물이어야합니다.

PicoSAT 라이브러리에 대한 증분 인터페이스를 활용하여 추가 된 구속 조건을 포함한 솔버를 즉시 재실행하여 솔버가 수행 한 모든 이전 추론을 유지합니다. PicoSAT는 새로운 모델을 제공하며 솔루션이 유효해질 때까지 위의 단계를 계속 반복합니다.

이것은 매우 효과적입니다. 1 초에 15 × 15 및 20 × 20 인스턴스를 해결합니다.

( 증분 SAT 솔버에서 연결된 영역을 대화식으로 제한한다는 아이디어를 제안한 Lopsy 에게 감사합니다 .)

puzzle-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에서 사용할 수없는 것 같습니다.

나는 아직 ungolfed, 기능적인 버전을 여기에 저장합니다. 의견에 따라 관심을 끌기 위해 설명을 줄이거 나 설명을 추가하려고 할 수 있습니다. 지금까지 제약 조건 이름은 전체 문서 역할을합니다.

주요 아이디어는 힌트 위치에 미리 지정된 소스가있는 보존 된 흐름 변수를 도입하여 "인접한 섬"제약 조건을 인코딩하는 것입니다. is_island그런 다음 의사 결정 변수 는 배치 가능한 싱크의 역할을합니다. 이 흐름의 총합을 최소화함으로써 섬들은 최적의 연결 상태를 유지해야합니다. 다른 제약 조건은 다양한 규칙을 직접 구현합니다. 내가 아직도 필요한 것 같은 퍼즐이 무엇입니까 island_fields_have_at_least_one_neighbor.

그러나,이 제형의 성능은 크지 않다. 많은 양의 제약 조건으로 모든 복잡성을 직접 먹음으로써 솔버는 15 x 15 예제의 경우 15 초 정도 걸립니다.

코드 (여전히 ungolfed)

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

용법

glpsol하나 개의 파일 (예로서, 모델을 설치 river.mod), 별도의 파일 (들) (예에서 입력 데이터 7x7.mod). 그때:

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

이렇게하면 약간의 인내심으로 솔루션을 지정된 출력 형식 ( "일부"진단 출력과 함께)으로 인쇄합니다.


2
다른 사람들이 그것이 효과가 있는지 확인할 수 있기 때문에이 답변은 경쟁하는 것으로 간주되어야한다고 생각합니다. IO 형식의 차이점은 합리적인 IO 형식을 수용해야한다는 가정하에 다루어야합니다.
fəˈnɛtɪk

2
@ fəˈnɛtɪk 동의합니다. TIO에서 실행할 수있는 답변이 필요한 ngn의 현상금에는 해당되지 않았지만 질문 자체의 요구 사항은 아닙니다.
Anders Kaseorg

내가 골프를 시작하지 않았다는 것을 감안할 때, 나는 아직 경쟁을 고려하지 않을 것이다. 모든 중복 제약 조건을 정리했으면 모든 선언을 한 번 수행합니다.
ojdo

3

파이썬 3 , 1295 바이트

이것은 파이썬 전용 솔루션입니다. 라이브러리를 사용하지 않고 표준 입력을 통해 보드를로드합니다. 앞으로 추가 설명. 이것은 큰 골프에서 처음 시도한 것입니다. 하단에 주석이 달린 골프 용 코드에 대한 링크가 있습니다.

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

온라인으로 사용해보십시오!

상기 봐 않은 golfed 코드 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.