게으른 전함 배치


39

다음 시나리오를 상상해보십시오. 친구와 전함을 플레이하고 있지만 속임수로 결정합니다. 배가 원래 있던 곳을 쏘고 나서 배를 움직이기보다는 배를 전혀 두지 않기로 결정합니다. 당신은 그와 같은 방식으로 배를 배치하는 것이 불가능할 때까지 그의 모든 샷이 미스라고 말합니다.

필드 크기, 선박 크기 목록 및 샷 목록이라는 세 가지 인수를 취하는 함수 또는 전체 프로그램을 작성해야합니다.

싸움터

주어진 매개 변수 중 하나는 보드 크기입니다. 전장은 세포의 제곱이며, 주어진 매개 변수는 단순히 광장의 한면입니다.
예를 들어, 다음은 5 크기의 보드입니다.

필드의 좌표는 문자 뒤에 숫자가 오는 2 요소 문자열로 지정됩니다. 어떤 경우에는 글자에 의존 할 수 있습니다.
문자는 열을 지정하고 숫자는 셀의 행을 지정합니다 (1 색인). 예를 들어 위 그림에서 강조 표시된 셀은로 표시됩니다 "D2".
문자는 26 자이므로 필드는 26x26보다 클 수 없습니다.

배들

선박은 1 개 이상의 블록으로 이루어진 직선입니다. 배송량은 목록에 지정되어 있으며, 첫 번째 요소는 1 셀 선박, 2 셀-2 셀 선박 등의 수량입니다.
예를 들어, 목록 [4,1,2,0,1]은 다음과 같은 배송을 생성합니다.

전장에 배치 된 선박은 서로 교차하거나 심지어 서로 접촉 할 수 없습니다. 모서리가 없어도. 그러나 필드의 가장자리를 만질 수 있습니다.
다음은 유효한 선박 배치의 예입니다.

주어진 배송에 대해 항상 주어진 크기의 빈 보드에 배치가 있다고 가정 할 수 있습니다.

산출

그러한 배의 배치가 존재하면, 그 중 하나를 출력해야합니다.
이 프로그램은 빈 셀을 나타내는 1 개, 선박 부품을 나타내는 1 개, "누락 된"것으로 표시된 셀의 3 가지 유형 중 하나로 개행으로 구분 된 ASCII 문자 행렬을 출력해야합니다. 다른 문자는 출력하지 않아야합니다.
예를 들어

ZZ@Z
\@@Z
@\\Z
\Z\\

(이 예에서는 @빈 셀로 정의 했습니다.\ "누락 된"셀, Z배송 부품으로 정의)

그러한 배치가 존재하지 않으면, 프로그램 / 기능은 아무것도 출력하지 않고 리턴되어야합니다.

입력

본격적인 프로그램을 작성하기로 결정했다면,리스트가 어떻게 입력되는지, 일부는 인수를 통해, 일부는 stdin을 통해 지정할 수 있습니다.

이것은 이며 가장 적은 양의 문자가 이깁니다.

비 골프 최적화 솔루션의 예는 여기에서 확인할 수 있습니다 .로
컴파일 하십시오-std=c99 . 첫 번째 인수는 보드의 크기이고 다른 인수는 선박 크기입니다. 줄 바꿈으로 구분 된 샷 목록은 stdin에 제공됩니다. 예:
./a 4 1 1 1 <<< $'A2\nA4\nB3\nC3\nC4\D4'


26x26? 정규 표현식과 재귀를 기반으로 솔루션을 스케치했으며 6x6. 보다 많은 필드에서는 사용할 수 없습니다 . 내가 멍청한 짓을하거나 답이 없으면 다른 사람들도 성공하지 못했음을 의미합니다.
user2846289

방금 C로 구현을 썼는데, 적어도 10x104,3,2,1번의 배송으로 즉시 계산합니다
mniip

@ mniip : 하드 케이스 (대형 보드, 많은 배, 샷 위치로 인해 실패)를 처리하는 특정 방법이 있습니까? 아니면 그냥 (약간 똑똑한) 무차별적인 힘입니까?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

그것은 몇 가지 최적화를 가지고 있으며, 소형 선박을 먼저 배치하려고 시도하고 동일한 크기의 선박을 무차별 대입에서 제외시킵니다. 작고 거의 빈 보드에 많은 배가있는 경우 다소 느립니다.
mniip

@ n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ 예제 솔루션 링크를 추가했습니다.
mniip

답변:


2

GolfScript, 236 자

n%([~](:N):M;[.,,]zip{~[)]*~}%-1%:S;{(65-\~(M*+}%:H;{{M*+}+N,/}N,%H-S[]]]{(~\.{(:L;{{M*+{+}+L,%}+N)L-,/}N,%{.{.M/\M%M*+}%}%{3$&,L=},{:K[{..M+\M-}%{..)\(}%3$\- 1$3$K+]}%\;\;\;\+.}{;:R;;;0}if}do{{{M*+.H?)'!'*\R?)'#'*'.'++1<}+N,/n}N,%}R!!*

입력은 STDIN에 제공됩니다. 첫 번째 줄에는 크기와 선박 수가 포함되며, 각 줄은 단일 샷의 좌표를 따릅니다.

예:

4 1 1 1
A2
A4
B3
C3
C4
D4

##.#
!..#
#!!#
!.!!

또한이 과제에는 적어도 하나의 GolfScript 답변이 있어야한다고 생각했습니다. 결국, 사용 된 알고리즘으로 인해 성능이 부족하여 사용률이 매우 떨어지게되었습니다.

주석이 달린 코드 :

n%               # Split the input into lines
([~]             # The first line is evaluated to an array [N S1 S2 S3 ...]
(:N              # This happy smiley removes the first item and assigns it to variable N
):M;             # While this sad smiley increases N by one and assigns it to M

[.,,]zip         # For the rest of numbers in the first line create the array [[0 S1] [1 S2] [2 S3] ...]
{~[)]*~}%        # Each element [i Si] is converted into the list (i+1 i+1 ... i+1) with Si items. 
-1%:S;           # Reverse (i.e. largest ship first) and assign the list to variable S.
                 # The result is a list of ship lengths, e.g. for input 3 0 2 we have S = [3 3 1 1 1].

{                # On the stack remains a list of coordinates
  (65-           # Convert the letter from A,B,... into numeric value 0,1,...
  \~(            # The number value is decreased by one
  M*+            # Both are combined to a single index (M*row+col)
}%:H;            # The list of shots is then assigned to variable H

                 # The algorithm is recursive backtracking implemented using a stack of tuples [A S R] where
                 #   - A is the list of open squares
                 #   - S is a list of ships to be placed
                 #   - R is the list of positions where ships were placed                     

    {{           # initial A is the full space of possible coordinates
      M*+        #   combine row and column values into a single index
    }+N,/}N,%    # do the N*N loop
    H-           # minus all places where a shot was done already
    S            # initial S is the original list
    []           # initial R is the empty list (no ships placed yet)
  ]
]                # The starting point is pushed on the stack 

{                # Start of the loop running on the stack
  (~\            # Pop from the stack and extract items in order A R S

  .{             #   If S is non-empty

    (:L;         #     Take first item in S (longest ship) and asign its length to L

    {{M*+{+}+L,%}+N)L-,/}N,%{.{.M/\M%M*+}%}%
                 #     This lengthy expression just calculates all possible ship placements on a single board
                 #     (could be shortened by 3 chars if we don't respect board size but I find it clearer this way)

    {3$&,L=},    #     This step is just a filter on those placements. The overlap (&) with the list of open squares (3$) 
                 #     must be of size L, i.e. all coordinates must be free

                 #     Now we have possible placements. For each one generate the appropriate tuple (A* S* R*) for recursion
    {
      :K         #     Assign the possible new ship placement to temporary variable K
      [
        {..M+\M-}%
        {..)\(}% 
                 #       For each coordinate add the square one row above and below (first loop)
                 #       and afterwards for the resulting list also all squares left and right (second loop)
        3$\-     #       Remove all these squares from the list of available squares A in order to get the new A*
        1$       #       Reduced list of ships S* (note that the first item of S was already remove above)
        3$K+     #       Last item in tuple is R* = R + K, i.e. the ship's placements are added to the result
      ]
    }%           

    \;\;\;       #     Discard the original values A R S
    \+           #     Push the newly generated tuples on the stack
    .            #     Loop until the stack is empty

  }{             #   else

    ;:R;;;       #     Assign the solution to the variable R and remove all the rest from the stack. 
    0            #     Push a zero in order to break the loop

  }if            #   End if

}do              # End of the loop


{                # The output block starts here
  {{             
    M*+
    .H?)         #   Is the current square in the list of shots?
    '!'*         #     then take a number of '!' otherwise an empty string
    \R?)         #   Is the current square in the list of ships?
    '#'*         #     then take a number of '#' otherwise an empty string
    '.'++        #   Join both strings and append a '.'
    1<           #   Take the first item of the resulting string, i.e. altogether this is a simple if-else-structure
  }+N,/n}N,%     # Do a N*N loop
}
R!!*             # Run this block only if R was assigned something during the backtracking. 
                 # (!! is double-negation which converts any non-zero R into a one)
                 # Note: since the empty list from the algorithm is still on the stack if R wasn't assigned
                 # anything the operator !! works on the code block (which yields 1) which is then multiplied
                 # with the empty list.

6

SWI- 프롤로그, 536 441 1 바이트

1 UNIX 줄 끝, 마지막 줄 바꿈

모든 최적화가 제거 된 버전 ( 441 바이트) :

:-[library(clpfd)].
m(G,L):-maplist(G,L).
l(L,A):-length(A,L).
y(A,E,(X,Y)):-nth1(X,A,R),nth1(Y,R,F),var(F),F=E.
a(A,S):-l(L,A),X#>0,X#=<L,Y#>0,Y#=<L,h(S,(X,Y),A).
h(0,_,_).
h(L,(X,Y),A):-(B=A;transpose(A,T),B=T),y(B,s,(X,Y)),M#=L-1,Z#=Y+1,h(M,(X,Z),B).
e([],_,[]).
e([H|R],I,O):-J#=I+1,e(R,J,P),l(H,Q),Q ins I,append(P,Q,O).
r(R):-m(c,R),nl.
c(E):-var(E)->put(@);put(E).
g(L,H,S):-l(L,A),m(l(L),A),m(y(A,\),S),e(H,1,G),!,m(a(A),G),!,m(r,A).

바이트 수를 최소화하도록 코드가 변경되었으므로 더 이상 중복 된 샷 목록을 허용하지 않습니다.


완전 최적화 된 기본 최적화 버전 ( 536 → 506 바이트)

:-[library(clpfd)].
m(G,L):-maplist(G,L).
l(L,A):-length(A,L).
x(A,I,E):-X=..[a|A],arg(I,X,E).
y(A,E,(X,Y)):-x(A,X,R),x(R,Y,E).
a(A,S):-l(L,A),X#>0,X#=<L,Y#>0,Y#=<L,(B=A;transpose(A,T),B=T),h(S,(X,Y),B).
h(0,_,_).
h(L,(X,Y),A):-y(A,E,(X,Y)),var(E),E=s,M#=L-1,Z#=Y+1,h(M,(X,Z),A).
e([],_,[]).
e([H|R],I,O):-J#=I+1,e(R,J,P),l(H,Q),Q ins I,append(P,Q,O).
r(R):-m(c,R),nl.
c(E):-var(E)->put(@);put(E).
g(L,H,S):-l(L,A),m(l(L),A),sort(S,T),m(y(A,\),T),e(H,1,G),!,l(E,T),sumlist(G,D),L*L-E>=D,m(a(A),G),!,m(r,A).

분명히 불가능한 경우 코드가 더 빨리 종료되도록 몇 가지 기본 검사 ( 필요한 선박 블록 수 )를 구현합니다 . 코드는 지금까지 샷 목록에서 복제 되지 않습니다.


아래는 자세한 설명과 함께 읽을 수있는 버전입니다.

:-[library(clpfd)].

% Shorthand for maplist, which works like map in high order function
m(G,L):-maplist(G,L).

% Creating a square matrix A which is L x L
board(L,A):-l(L,A),m(l(L),A).

% Shorthand for length, with order of parameters reversed
l(L,A):-length(A,L).

% Unification A[I] = E
x(A,I,E):-X=..[a|A],arg(I,X,E).

% Unification A[X][Y]=E
idx2(A,E,(X,Y)):-x(A,X,R),x(R,Y,E).

% Mark positions that have been shot
markshot(A,S):-m(idx2(A,\),S).

% Place all ships on the board
placeships(H,A):-m(placeship(A),H).

% Place a length S ship horizontal/vertical forward on the board
placeship(A,S):-
    l(L,A), % Get length
    X#>0,X#=<L,Y#>0,Y#=<L, % Constraint X and Y coordinates
    transpose(A,T), % Transpose to work on columns
    (placeship_h(S,(X,Y),A) ; placeship_h(S,(Y,X),T)).

% Place ship horizontal, forward at (X,Y)
placeship_h(0,_,_).
placeship_h(L,(X,Y),A):-
    idx2(A,E,(X,Y)),var(E),E=s, % Make sure position is unassigned, then mark
    L2#=L-1,Y2#=Y+1, % Do this for all blocks of the ship
    placeship_h(L2,(X,Y2),A).

% Expand the list of ships
% e.g. [2,3,1] --> [3,2,2,2,1,1]
shipexpand(S,O):-shipexpand(S,1,O).

shipexpand([],_,[]).
shipexpand([H|R],I,O):-
    I2#=I+1,shipexpand(R,I2,O2), % process the rest
    l(H,O1),O1 ins I, % duplicate current ship size H times
    append(O2,O1,O). % larger ship size goes in front

% Print the result
pboard(A):-m(prow,A).
prow(R):-m(pcell,R),print('\n').
pcell(E):-var(E)->print(@);print(E).

game(L,H,S):-
    board(L,A), % create board
    sort(S,SS), % remove duplicates
    markshot(A,SS), % mark positions that have been shot
    shipexpand(H,HH),!, % make a list of ships
    l(SC,SS),sumlist(HH,BC),L*L-SC>=BC, % check to speed-up, can be removed
    placeships(HH,A),!, % place ships
    pboard(A). % print result

쿼리 형식 :

game(Board_Size, Ships_List, Shots_List).

샘플 쿼리 (질문에 샘플 사용) :

?- game(4,[1,1,1],[(2,1),(3,2),(3,3),(4,1),(4,3),(4,4)]).
ssss
\ss@
@\\@
\@\\
true.

?- game(4,[2,2,0,1],[(2,1),(3,2),(3,3),(4,1),(4,3),(4,4)]).
ssss
\sss
s\\s
\s\\
true.

아, 수십 명의 캐릭터가 나를 때렸습니다! 내가 더 이상 압축 할 수 있는지 확실하지 않지만 계속 노력할 것입니다 ... 프롤로그를 잘 사용하십시오!
Claudiu

@Claudiu : 내 솔루션에는 여전히 20 자 정도의 "버퍼"가 있습니다. 성능상의 이유로 검사 코드를 그대로 두었지만 정확성에 영향을주지 않고 제거 할 수 있습니다.) 다른 답변이 500 미만이면 귀찮게하지 않습니다.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

5

MATLAB-536 자

업데이트 : 훨씬 작은 출력 형식, 일부 루프 공백이 제거되었습니다.

업데이트 : 골프 버전 추가

업데이트 : 댓글 버전을 추가하고 골프 버전을 100 자까지 줄였습니다.

% Battleship puzzle solver.
%
% n: size of the map (ex. 4 -> 4x4)
% sh: list of shots (ex. ['A2';'C4'])
% sp: ships of each size (ex. [2,0,1] -> 2x1 and 1x3)
%
function bs(n,sh,sp)

  % sp holds a vector of ship counts, where the index of each element is
  % the size of the ship. s will hold a vector of ship sizes, with order
  % not mattering. This is easier to work with using recursion, because
  % we can remove elements with Matlab's array subselection syntax, rather
  % than decrement elements and check if they're zero.
  %
  % Tricks:
  %   Since sp never contains a -1, find(1+sp) is the same as 1:length(sp)
  %   but is three characters shorter.
  %
  s=[];
  for i=find(1+sp)
    s(end+1:end+sp(i))=i;
  end


  % m will hold the map. It will be +2 in each direction, so that later we
  % can find neighbors of edge-spaces without checking bounds. For now,
  % each element is either '0' or '1' for a space or missed shot,
  % respectively. We convert the shots as provided by the user (ex. 'A2')
  % to marks on the map.
  %
  % Tricks:
  %   It takes one shorter character to subtract 47 than 'A' to determine
  %   the indices into the map.
  %
  m=zeros(n+2);
  for h=sh'
    m(h(2)-47,h(1)-63)=1;
  end


  % Solve the puzzle. q will either be the empty array or a solution map.
  %
  q=pp(m,s);


  % If a solution was found, output it, showing the original shots and the
  % ship placement. If no solution was found, print a sad face.
  %
  % Tricks:
  %   q is 0 on failure, which is treated like 'false'. q is a matrix on
  %   success which is NOT treated like 'true', so we have to check for
  %   failure first, then handle success in the 'else' block.
  %
  %   q contains the "fake shots" that surround each placed ship (see the
  %   pl function). We don't want to output those, so we just copy the ship
  %   markings into the original map.
  %
  if ~q ':('
  else
  m(find(q==2))=2;
  num2str(m(2:n+1,2:n+1),'%d')
  end



% Depth-first search for a solution.
%
% m: map (see main code above)
% s: vector of ship sizes to place in the map
%
% Returns q: square matrix of integers, updated with all requested ship
% sizes, or 0 if no solution is possible.
%
function q = pp(m,s)

  % If we have no ships to process, we're all done recursing and the
  % current map is a valid solution. Rather than handle this in an 'else'
  % block, we can assume it's the case and overwrite q if not, saving 4
  % characters.
  %
  q=m;


  % If we have any ships to place...
  %
  % Tricks:
  %   Since we are only interested in positive values in s, we can use
  %   sum(s) in place of isempty(s), saving 4 characters.
  %
  if sum(s)

    % Try to place the first ship in the list into the map, both vertically
    % (first call to p) and vertically (second call to p). We can process
    % any ship in the list, but the first can be removed from the list
    % with the fewest characters. r will hold a cell-array of options for
    % this ship.
    %
    r=[p(m,s(1),0),p(m',s(1),1)];


    % Recurse for each option until we find a solution.
    %
    % Tricks:
    %   Start with q, our return variable, set to 0 (indicating no solution
    %   was found. On each loop we'll only bother to recurse if q is still
    %   0. This relieves the need for if/else to check whether to continue
    %   the loop, or any final q=0 if the loop exits without success.
    %
    %   Sadly, there's no way around the length(r) call. Matlab doesn't
    %   provide syntax for iterating over cell-arrays.
    %
    q=0;
    for i=1:length(r)
      if ~q q=pp(r{i},s(2:end));end
    end
  end



% Place a single ship into a map.
%
% m: map (see main code above)
% s: ship size to place
% t: if the map has been transposed prior to this call
%
% Returns r: cell-array of possible maps including this ship
%
function r=p(m,s,t)
  % Start with an empty cell-array and pre-compute the size of the map,
  % which we'll need to use a few times.
  %
  r={};
  z=size(m);


  % For each row (omitting the first and last 'buffer' rows)...
  %
  for i=2:z(2)-1

  % For each starting column in this row where enough consecutive 0s
  % appear to fit this ship...
  %
  for j=strfind(m(i,2:end-1),(1:s)*0)

    % Copy the map so we can modify it without overwriting the variable
    % for the next loop.
    %
    n=m;


    % For each location on the map that is part of this optional
    % placement...
    for l=0:s-1
      % Let's leave this is an exercise for the reader ;)
      %
      v=-1:1;
      n([(l+j)*z(1)+i,z(1),1]*[ones(1,9);kron(v,[1 1 1]);[v v v]])=1;
    end


    % Mark each location that is part of this optional placement with
    % a '2'.
    %
    n(i,1+j:j+s)=2;


    % Since our results are going into a cell-array it won't be
    % convenient for the caller to undo any transpositions they might
    % have done. If the t flag is set, transpose this map back before
    % adding it to the cell-array.
    %
    if t n=n';end
    r{end+1}=n;
  end
  end

골프 버전입니다.

function bs(n,sh,sp)
s=[];for i=find(1+sp)
s(end+1:end+sp(i))=i;end
m=zeros(n+2);for h=sh'
m(h(2)-47,h(1)-63)=1;end
q=pp(m,s);if ~q ':('
else
m(find(q==2))=2;num2str(m(2:n+1,2:n+1),'%d')
end
function q = pp(m,s)
q=m;if sum(s)
r=[p(m,s(1),0),p(m',s(1),1)];q=0;for i=1:length(r)if ~q q=pp(r{i},s(2:end));end
end
end
function r=p(m,s,t)
r={};z=size(m);for i=2:z(2)-1
for j=strfind(m(i,2:end-1),(1:s)*0)n=m;for l=0:s-1
v=-1:1;n([(l+j)*z(1)+i,z(1),1]*[ones(1,9);kron(v,[1 1 1]);[v v v]])=1;end
n(i,1+j:j+s)=2;if t n=n';end
r{end+1}=n;end
end

샘플은 다음과 같습니다.

>>bs(4,['A1';'B3'],[2,1])
1220
0000
2120
0000

>>bs(4,['A1';'B4'],[2,2])
1022
2000
0022
2100

>> bs(4,['A1';'B4';'C2'],[3,1])
1022
2010
0020
2100

>> bs(4,['A1';'B4';'C2'],[3,2])
:(

'kron'(골프가없는 코드의 하단 근처)이있는 큰 줄이 내가 가장 좋아하는 부분입니다. 주어진 위치에 인접한지도의 모든 위치에 '1'을 씁니다. 어떻게 작동하는지 볼 수 있습니까? 크로네 커 텐서 곱, 행렬 곱셈을 사용하고 맵을 선형 배열로 인덱싱합니다 ...


4

파이썬, 464 자

B,L,M=input()
R=range(B)
C=B+1
M=sum(1<<int(m[1:])*C-C+ord(m[0])-65for m in M)
def P(L,H,S):
 if len(L)==0:
  for y in R:print''.join('.#!'[(S>>y*C+x&1)+2*(M>>y*C+x&1)]for x in R)
  return 1
 for s in[2**L[0]-1,sum(1<<C*j for j in range(L[0]))]:
  for i in range(C*C):
   if H&s==s and P(L[1:],H&~(s|s<<1|s>>1|s<<B|s>>B|s<<C|s>>C|s<<C+1|s>>C+1),S|s):return 1
   s*=2
 return 0
P(sum(([l+1]*k for l,k in enumerate(L)),[])[::-1],sum((2**B-1)<<B*j+j for j in R)&~M,0)

입력 (stdin) :

7, [4,1,2,0,1], ['A1','B2','C3']

산출:

!#####.
.!.....
##!###.
.......
###.#.#
.......
#.#....

다양한 기능의 비트 맵을 보유하는 정수를 사용하여 작동합니다.

M = bitmap of misses
H = bitmap of holes where ships can still go
S = bitmap of ships already placed
L = list of ship sizes not yet placed
B = dimension of board
C = bitmap row length

마음에 들지 않으면 최적화를 수행합니까, 아니면 그냥 무차별적인 힘입니까?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

한 가지 최적화가 있습니다 [::-1]. 가장 긴 배를 가장 먼저 시도합니다. 또한 배치 할 수없는 선박을 발견하자마자 역 추적합니다.
Keith Randall

7, 8, 11 및 12 행에서 2 또는 3 개의 공백 대신 단일 탭을 사용하여 바이트 수를 458로 줄일 수 있습니다 . 여기를 참조 하십시오 .
mbomb007

3

파이썬, 562 자, 못생긴 결과물을 가진 -8, +4? bash 호출

I=int;R=range
import sys;a=sys.argv
S=I(a[1]);f=[[0]*(S+2)for _ in R(S+2)]
C=a[2].split()
X=[]
for i,n in enumerate(C):X=[i+1]*I(n)+X
Q=a[3].split()
for q in Q:f[I(q[1:])][ord(q[0])-64]=1
D=R(-1,2)
V=lambda r,c:(all(f[r+Q][c+W]<2for Q in D for W in D),0,0)[f[r][c]]
def F(X):
 if not X:print"\n".join(" ".join(" .@"[c]for c in r[1:-1])for r in f[1:-1])
 x=X[0];A=R(1,S+1)
 for r in A:
    for c in A:
     for h in(0,1):
        def P(m):
         for i in R(x):f[(r+i,r)[h]][(c,c+i)[h]]=m
        if(r+x,c+x)[h]<S+2and all(V((r+i,r)[h],(c,c+i)[h])for i in R(x)):P(2);F(X[1:]);P(0)
F(X)

참고 : 들여 쓰기 수준은 공백, 탭, 탭 + 공백, 탭 + 탭 및 탭 + 탭 + 공백입니다. 이렇게하면 공백 만 사용하지 않아도됩니다.

사용법과 예 :

명령 행 인수에서 입력을받습니다. 공백을 공백으로, 샷을 a .@배의 일부로 출력합니다 .

$ python bships_golf.py "7" "4 0 2 0 1" \
         "A1 C3 B5 E4 G6 G7 A3 A4 A5 A6 C1 C3 C5 C7 B6 B7 D1 D2 G3" 2>X
. @ . . @ @ @
  @   .
. @ . @   @ .
.     @ .
. . . @   @
. .   @     .
@ . . @   @ .

해결할 수없는 경우 아무것도 인쇄하지 않습니다.

$ python bships_golf.py "3" "2" "A1 A3 B1 C1 C3" 2>X
. . .
@   @
.   .
$ python bships_golf.py "3" "2" "A1 A2 A3 B1 C1 C3" 2>X
$

그만큼 2>X 예외를 throw하여 프로그램이 종료하기 때문에 오류 메시지를 표시하는 것입니다. 공정하다고 간주되면 +4 페널티를 추가하십시오. 그렇지 않으면 try: ... except:0어쨌든 더 많은 문자를 사용하기 위해 억제 하지 않아야합니다.

나는 또한 숫자로 출력을 인쇄 할 수 있습니다 ( 0, 1, 및2 ) 8 자 면도하는,하지만 값 미학.

설명 :

경계 검사를 수행하지 않기 위해 입력보다 크기 2가 큰 정수 목록의 목록으로 보드를 나타냅니다. 0빈 공간, 1샷 및 2배를 나타냅니다 . 샷 목록을 살펴보고 Q모든 샷을 표시합니다. 배송 목록을 명시 적 배송 목록으로 변환합니다 ( X예 : [4, 0, 2, 0, 1]goes) [5, 3, 3, 1, 1, 1, 1]. 그런 다음 간단한 역 추적 알고리즘입니다. 내림차순으로 배를 배치 한 다음 나머지 배를 배치하십시오. 작동하지 않으면 다음 슬롯을 사용해보십시오. 성공하자마자 배송 목록 X은 null이며 액세스 X[0]하면 프로그램을 종료하는 예외가 발생합니다. 나머지는 단지 무거운 골프입니다 (처음에는 1615 자였습니다).


2

펄, 455 447 437 436 422 418

$n=($N=<>+0)+1;@S=map{(++$-)x$_}<>=~/\d+/g;$_=<>;$f=('~'x$N.$/)x$N;substr($f,$n*$1-$n-65+ord$&,1)=x while/\w(\d+)/g;sub f{for my$i(0..$N*$n-1){for(0..@_-2){my($f,@b)=@_;$m=($s=splice@b,$_,1)-1;pos=pos($u=$_=$f)=$i;for(s/\G(~.{$N}){$m}~/$&&("\0"."\377"x$N)x$s|(o."\0"x$N)x$m.o/se?$_:(),$u=~s/\G~{$s}/o x$s/se?$u:()){for$k(0,$N-1..$n){s/(?<=o.{$k})~|~(?=.{$k}o)/!/sg}return$:if$:=@b?f($_,@b):$_}}}}$_=f$f,@S;y/!/~/;print

들여 쓰기 :

$n=($N=<>+0)+1;
@S=map{(++$-)x$_}<>=~/\d+/g;
$_=<>;
$f=('~'x$N.$/)x$N;
substr($f,$n*$1-$n-65+ord$&,1)=x while/\w(\d+)/g;
sub f{
    for my$i(0..$N*$n-1){
        for(0..@_-2){
            my($f,@b)=@_;
            $m=($s=splice@b,$_,1)-1;
            pos=pos($u=$_=$f)=$i;
            for(s/\G(~.{$N}){$m}~/
              $&&("\0"."\377"x$N)x$s|(o."\0"x$N)x$m.o/se?$_:(),
              $u=~s/\G~{$s}/o x$s/se?$u:()){
                for$k(0,$N-1..$n){
                    s/(?<=o.{$k})~|~(?=.{$k}o)/!/sg
                }
                return$:if$:=@b?f($_,@b):$_
            }
        }
    }
}
$_=f$f,@S;
y/!/~/;
print

나는 그것이 더 골프화 될 수 있다고 기대한다 (예를 들어, eval<>사전 포맷 된 입력의 경우 OK (?)), 그리고 다른 것들 (50 $sigils? 아니, 그들은 머무를 것이다).

앞에서 말했듯이 솔루션이 재귀 트리의 위치에 따라 (순간 (아래 예 참조) 매우 긴 시간까지) 속도가 문제가 될 수 있지만 사용 된 하드웨어의 노후화 문제가됩니다. 나중에 재귀가 사라지고 2-3 가지 다른 속임수가있는 최적화 된 버전을 수행 할 것입니다.

STDIN을 통해 3 줄이 공급됩니다.

$ perl bf.pl
7
4 1 2 0 1
A1 B2 C3
xo~o~o~
~x~~~~~
o~xo~o~
~~~o~o~
o~~~~o~
o~~~~~~
o~ooooo

~바다 (예술 솔루션이 아닌 것이있다)이며, o의와 x의 선박과 촬영입니다. 처음 5 줄은 입력을 받고 '전장'을 준비합니다. $N크기 @S가 크며 , 1 1 1 1 2 3 3 5위와 같이 배의 펼쳐진 배열이며 , 다음 반복으로 분기됩니다. 등등.$f 전장을 나타내는 문자열입니다 (줄 바꾸기가 연결된 행). 다음은 재귀 서브 루틴으로, 현재 전장 상태와 남은 선박 목록이 필요합니다. 왼쪽에서 오른쪽으로, 위에서 아래로 점검하고 각 선박을 수평 및 수직으로 각 위치에 배치하려고 시도합니다 (최적화를 위해 잘 익은 참조). 수평선은 명백한 정규 표현식 대체물이며, 수직은 조금 까다 롭습니다- '열'에서 바꿀 비트 문자열 조작. 성공하면 (H, V 또는 둘 다) 액세스 할 수없는 새로운 위치에 표시됩니다!

편집 : 좋아, 여기에 594- 에 실제로 유용한 (즉, 빨리) 노력하는 바이트 ( 않은 경우) 버전이 있습니다. 동일한 기술을 구현하면서도 내 능력을 최대한 발휘하도록 재귀 ( '수동으로') 및 정규 표현식. '스택'을 유지합니다.@A-조사 할 가치가있는 상태의 배열. '상태'는 4 가지 변수입니다 : 현재 전장 문자열, 선박을 배치하기 시작한 곳의 색인, 남은 선박의 배열에 대한 참조, 다음에 시도 할 선박의 색인. 처음에는 하나의 '상태'가 있습니다-빈 문자열의 시작, 모든 선박. 일치하면 (H 또는 V, 위 참조), 나중에 조사하기 위해 현재 상태가 푸시되고, 선박이 배치되고 접근 할 수없는 위치가 표시된 업데이트 된 상태가 푸시되고 블록이 다시 시작됩니다. 전장 끝에 도달하면 성공할 수있는 다음 상태를 @A조사합니다 (있는 경우).

다른 최적화는 : 문자열의 맨 처음부터 다시 시작하지 않고, 큰 선박을 먼저 배치하려고 시도하며, 이전 선박이 동일한 위치에서 실패한 경우 동일한 크기의 선박을 검사하지 않고 + 다른 $&변수 (예 : 변수 없음 ).

$N=<>+0;
$S=[reverse map{(++$-)x$_}<>=~/\d+/g];
$_=<>;
$f=('~'x$N.$/)x$N;
substr($f,$N*$2-$N+$2-66+ord$1,1)=x while/(\w)(\d+)/g;
push@A,$f,0,$S,0;
O:{
    ($f,$i,$S,$m)=splice@A,-4;
    last if!@$S;
    F:{ 
        for$j($m..$#$S){
            next if$j and$$S[$j]==$$S[$j-1];
            $s=$$S[$j];
            my@m;
            $_=$f;
            $n=$s-1;
            pos=$i;
            push@m,$_ if s/\G(?:~.{$N}){$n}~/
                ${^MATCH}&("\0"."\377"x$N)x$s|(o."\0"x$N)x$n.o/pse;
            $_=$f;
            pos=$i;
            push@m,$_ if s/\G~{$s}/o x$s/se;
            if(@m){
                push@A,$f,$i,$S,$j+1;
                my@b=@$S;
                splice@b,$j,1;
                for(@m){
                    for$k(0,$N-1..$N+1){
                        s/(?<=o.{$k})~|~(?=.{$k}o)/!/gs
                    }
                    push@A,$_,$i+1,\@b,0
                }
                redo O
            }
        }
        redo if++$i-length$f
    }
    redo if@A
}
print@$S?'':$f=~y/!/~/r

.

perl bf+.pl
10
5 4 3 2 1
A1 B2 C3 A10 B9 C10 J1 I2 H3 I9 J10 A5 C5 E5 F6 G7
xooooo~oox
~x~~~~~~x~
ooxooooxo~
~~~~~~~~o~
xoxoxoo~o~
~o~o~x~~o~
~o~o~ox~~~
~~~~~o~ooo
oxo~~~~~x~
x~x~o~o~ox

OTOH, 6 5 4 3 2 1위의 예에서 불가능한 경우에는 여전히 영원합니다 . 총 선박 톤수가 전장 용량을 초과하면 실제 버전이 즉시 종료 될 수 있습니다.


2

C # 솔루션

  public static class ShipSolution {
    private static int[][] cloneField(int[][] field) {

      int[][] place = new int[field.Length][];

      for (int i = 0; i < field.Length; ++i) {
        place[i] = new int[field.Length];

        for (int j = 0; j < field.Length; ++j)
          place[i][j] = field[i][j];
      }

      return place;

    }

    private static void copyField(int[][] source, int[][] target) {
      for (int i = 0; i < source.Length; ++i)
        for (int j = 0; j < source.Length; ++j)
          target[i][j] = source[i][j];
    }

    // Check if placement a legal one
    private static Boolean isPlacement(int[][] field, int x, int y, int length, Boolean isHorizontal) {
      if (x < 0)
        return false;
      else if (y < 0)
        return false;
      else if (x >= field.Length)
        return false;
      else if (y >= field.Length)
        return false;

      if (isHorizontal) {
        if ((x + length - 1) >= field.Length)
          return false;

        for (int i = 0; i < length; ++i)
          if (field[x + i][y] != 0)
            return false;
      }
      else {
        if ((y + length - 1) >= field.Length)
          return false;

        for (int i = 0; i < length; ++i)
          if (field[x][y + i] != 0)
            return false;
      }

      return true;
    }

    //  When ship (legally) placed it should be marked at the field
    //  2 - ship itself
    //  3 - blocked area where no other ship could be placed
    private static void markPlacement(int[][] field, int x, int y, int length, Boolean isHorizontal) {
      if (isHorizontal) {
        for (int i = 0; i < length; ++i)
          field[x + i][y] = 2;

        for (int i = x - 1; i < x + length + 1; ++i) {
          if ((i < 0) || (i >= field.Length))
            continue;

          for (int j = y - 1; j <= y + 1; ++j)
            if ((j >= 0) && (j < field.Length))
              if (field[i][j] == 0)
                field[i][j] = 3;
        }
      }
      else {
        for (int i = 0; i < length; ++i)
          field[x][y + i] = 2;

        for (int i = x - 1; i <= x + 1; ++i) {
          if ((i < 0) || (i >= field.Length))
            continue;

          for (int j = y - 1; j < y + length + 1; ++j)
            if ((j >= 0) && (j < field.Length))
              if (field[i][j] == 0)
                field[i][j] = 3;
        }
      }
    }

    // Ship placement itteration
    private static Boolean placeShips(int[][] field, int[] ships) {
      int[] vessels = new int[ships.Length];

      for (int i = 0; i < ships.Length; ++i)
        vessels[i] = ships[i];

      for (int i = ships.Length - 1; i >= 0; --i) {
        if (ships[i] <= 0)
          continue;

        int length = i + 1;

        vessels[i] = vessels[i] - 1;

        // Possible placements
        for (int x = 0; x < field.Length; ++x)
          for (int y = 0; y < field.Length; ++y) {
            if (isPlacement(field, x, y, length, true)) {
              int[][] newField = cloneField(field);

              // Mark
              markPlacement(newField, x, y, length, true);

              if (placeShips(newField, vessels)) {
                copyField(newField, field);

                return true;
              }
            }

            if (length > 1)
              if (isPlacement(field, x, y, length, false)) {
                int[][] newField = cloneField(field);

                // Mark
                markPlacement(newField, x, y, length, false);

                if (placeShips(newField, vessels)) {
                  copyField(newField, field);

                  return true;
                }
              }
          }

        return false; // <- the ship can't be placed legally
      }

      return true; //    <- there're no more ships to be placed
    }

    /// <summary>
    /// Solve ship puzzle
    /// </summary>
    /// <param name="size">size of the board</param>
    /// <param name="ships">ships to be placed</param>
    /// <param name="misses">misses in (line, column) format; both line and column are zero-based</param>
    /// <returns>Empty string if there is no solution; otherwise possible ship placement where
    ///   . - Blank place
    ///   * - "miss"
    ///   X - Ship
    /// </returns>
    public static String Solve(int size, int[] ships, String[] misses) {
      int[][] field = new int[size][];

      for (int i = 0; i < size; ++i)
        field[i] = new int[size];

      if (!Object.ReferenceEquals(null, misses))
        foreach (String item in misses) {
          String miss = item.Trim().ToUpperInvariant();

          int x = int.Parse(miss.Substring(1)) - 1;
          int y = miss[0] - 'A';

          field[x][y] = 1;
        }

      if (!placeShips(field, ships))
        return "";

      StringBuilder Sb = new StringBuilder();

      foreach (int[] line in field) {
        if (Sb.Length > 0)
          Sb.AppendLine();

        foreach (int item in line) {
          if (item == 1)
            Sb.Append('*');
          else if (item == 2)
            Sb.Append('X');
          else
            Sb.Append('.');
        }
      }

      return Sb.ToString();
    }
  }

  ...

  int size = 4;
  int[] ships = new int[] { 1, 1, 1 };
  String[] misses = new String[] {"C1", "C2", "B2", "A3", "A1", "B3", "D1"};

  // *X**
  // .**X
  // **.X
  // XX.X
  Console.Out.Write(ShipSolution.Solve(size, ships, misses));

이것은 훌륭하고 빠른 솔루션이지만 해결할 수없는 작업을 올바르게 처리하지 못하는 것 같습니다. 예를 들면 다음과 같습니다 size=1 ships={1} moves={"A1"}.
mniip

죄송합니다. 다음 배를 합법적으로 배치 할 수없는 상황을 놓쳤습니다. 솔루션을 편집했습니다.
Dmitry Bychenko

6
질문은 code-golf 이므로 문자 수를 가능한 한 낮게 유지 (예 : 공백 제거)하고 문자 수를 코드에 포함 시키십시오.
ProgramFOX

현재 문자 수는 5399입니다.
intx13

1

자바, 1178

그래 너무 길다 ._.

import java.util.*;class S{public static void main(String[]a){new S();}int a;int[][]f;List<L>l;Stack<Integer>b;S(){Scanner s=new Scanner(System.in);a=s.nextInt();f=new int[a][a];for(int[]x:f)Arrays.fill(x,95);s.next(";");b=new Stack();for(int i=1;s.hasNextInt();i++){b.addAll(Collections.nCopies(s.nextInt(),i));}s.next(";");while(s.findInLine("([A-Z])")!=null)f[s.match().group(1).charAt(0)-65][s.nextInt()-1]=79;l=new ArrayList();for(int i=0;i<a;i++){l.add(new L(i){int g(int c){return g(c,i);}void s(int c,int v){f[c][i]=v;}});l.add(new L(i){int g(int r){return g(i,r);}void s(int r,int v){f[i][r]=v;}});}if(s()){String o="";for(int r=0;r<a;r++){for(int c=0;c<a;c++)o+=(char)f[c][r];o+='\n';}System.out.print(o);}}boolean s(){if(b.empty())return true;int s=b.pop();Integer n=95;for(L c:l){int f=0;for(int x=0;x<a;x++){if(n.equals(c.g(x)))f++;else f=0;if(f>=s){for(int i=0;i<s;i++)c.s(x-i,35);if(s())return true;for(int i=0;i<s;i++)c.s(x-i,n);}}}b.push(s);return false;}class L{int i;L(int v){i=v;}void s(int i,int v){}int g(int i){return 0;}int g(int c,int r){int v=0;for(int x=-1;x<2;x++)for(int y=-1;y<2;y++)try{v|=f[c+x][r+y];}catch(Exception e){}return v&(f[c][r]|32);}}}

언 골프 드 :

import java.util.*;

class S {
    public static void main(String[] a) {
        new S();
    }

    /**
     * Number of rows/columns
     */
    int a;

    /**
     * A reference to the full field
     */
    int[][] f;

    /**
     * List of Ls representing all columns/rows
     */
    List<L> l;

    /**
     * Stack of all unplaced ships, represented by their length as Integer
     */
    Stack<Integer> b;

    S() {
        // Read input from STDIN:
        Scanner s = new Scanner(System.in);
        a = s.nextInt();
        f = new int[a][a];
        // initialize field to all '_'
        for(int[] x: f)
            Arrays.fill(x, 95);
        // ; as separator
        s.next(";");
        b = new Stack();
        // Several int to represent ships
        for (int i = 1; s.hasNextInt(); i++) {
            // nCopies for easier handling (empty Stack => everything placed)
            b.addAll(Collections.nCopies(s.nextInt(), i));
        }
        // ; as separator
        s.next(";");
        // find an uppercase letter on this line
        while (s.findInLine("([A-Z])") != null) {
            // s.match.group(1) contains the matched letter
            // s.nextInt() to get the number following the letter
            f[s.match().group(1).charAt(0) - 65][s.nextInt() - 1] = 79;
        }
        // Loop is left when line ends or no uppercase letter is following the current position

        // Now create a List of Lists representing single columns and rows of our field
        l = new ArrayList();
        for (int i = 0; i < a; i++) {
            l.add(new L(i) {
                int g(int c) {
                    return g(c, i);
                }

                void s(int c, int v) {
                    f[c][i] = v;
                }
            });
            l.add(new L(i) {
                int g(int r) {
                    return g(i, r);
                }

                void s(int r, int v) {
                    f[i][r] = v;
                }
            });
        }
        // try to place all ships
        if (s()) {
            // print solution
            String o = "";
            for (int r = 0; r < a; r++) {
                for (int c = 0; c < a; c++) {
                    o += (char) f[c][r];
                }
                o += '\n';
            }
            System.out.print(o);
        }
    }

    /**
     * Try to place all ships
     *
     * @return {@code true}, if a solution is found
     */
    boolean s() {
        if (b.empty()) {
            // no more ships
            return true;
        }
        // take a ship from the stack
        int s = b.pop();
        // 95 is the Ascii-code of _
        Integer n = 95;
        // go over all columns and rows
        for (L c : l) {
            // f counts the number of consecutive free fields
            int f = 0;
            // move through this column/row
            for (int x = 0; x < a; x++) {
                // Is current field free?
                if (n.equals(c.g(x))) {
                    f++;
                } else {
                    f = 0;
                }
                // Did we encounter enough free fields to place our ship?
                if (f >= s) {
                    // enter ship
                    for (int i = 0; i < s; i++) {
                        c.s(x - i, 35);
                    }
                    // try to place remaining ships
                    if (s()) {
                        return true;
                    }
                    // placing remaining ships has failed ; remove ship
                    for (int i = 0; i < s; i++) {
                        c.s(x - i, n);
                    }
                }
            }
        }
        // we have found no place for our ship so lets push it back
        b.push(s);
        return false;
    }

    /**
     * List representing a single row or column of the field.
     * "Abstract"
     */
    class L {
        /**
         * Index of row/column. Stored here as loop-variables can not be final. Used only {@link #g(int)} and {@link #s(int, int)}
         */
        int i;

        L(int v) {
            i = v;
        }

        /**
         * Set char representing the state at the i-th position in this row/column.
         * "Abstract"
         */
        void s(int i, int v) {
        }

        /**
         * Get char representing the state at the i-th position in this row/column.
         * "Abstract"
         *
         * @return {@code '_'} if this and all adjacent field contain no {@code '#'}
         */
        int g(int i) {
            return 0;
        }

        /**
         * Get char representing the state at the position in c-th column and r-th row
         *
         * @return {@code '_'} if this and all adjacent field contain no {@code '#'}
         */
        int g(int c, int r) {
            // v stores the result
            int v = 0;
            // or all adjacent fields
            for (int x = -1; x < 2; x++) {
                for (int y = -1; y < 2; y++) {
                    // ungolfed we should use something like
                    // v |= 0 > c + x || 0 > r + y || c + x >= a || r + y >= a ? 0 : f[c + x][r + y];
                    // but his will do (and is shorter)
                    try {
                        v |= f[c + x][r + y];
                    } catch (Exception e) {
                    }
                }
            }
            // we use '_' (95), 'O' (79), '#' (35). Only 35 contains the 32-bit
            // So we only need the 32-bit of the or-ed-value + the bits of the value directly in this field
            return v & (f[c][r] | 32);
        }

    }
}

샘플 입력

6 ; 3 2 1 ; A1 A3 B2 C3 D4 E5 F6 B6 E3 D3 F4

샘플 출력

O###_#
_O____
O_OOO#
#_#O_O
#_#_O#
_O___O

  • O 놓친 샷
  • # 선박 부품
  • _ 다음에 쏴라 ;-)

ideone.com 참조

입력 처리는 숫자 주위의 공백을 예상하고 ;그렇지 않으면 작동하지 않습니다.

갈 곳이 적기 때문에 큰 배를 먼저 배치하고 있습니다. 전환하려는 경우 작은 먼저 교체 할 수 pop()remove(0)push(s)의해 add(0,s)심지어에만 두 중 하나가 여전히 유효한 프로그램 초래한다 교체.

선박이 서로 접촉하는 것을 허용하면 g(int,int)기능을 크게 단순화하거나 제거 할 수 있습니다.

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