현 건설 게임을위한 승리 전략 설정에서 발견 되면 Alice가

배경

Alice와 Bob 은 이진 단어를 구성하는 게임을 합니다. 게임을하려면 길이 n >= 0, 목표 세트 라고하는 G길이 n이진 단어 세트 및 문자를 포함 하는 길이 n문자열 과 턴 순서 라는 t문자를 포함합니다 . 게임은 턴 동안 지속되며, 턴에 의해 정의 된 플레이어 는 비트 를 선택합니다 . 게임이 끝나면 플레이어는 자신이 만든 이진 단어 를 봅니다 . 이 단어가 목표 설정에서 발견 되면 Alice가 게임에서 승리합니다. 그렇지 않으면 밥이 이깁니다.ABnit[i]w[i]wG

예를 들어, 수정하자 n = 4, G = [0001,1011,0010]등을 t = AABA. 앨리스는 첫 턴을했고 그녀는을 선택합니다 w[0] = 0. 두 번째 차례는 Alice의 것이며, 그녀는를 선택합니다 w[1] = 0. 밥은 세 번째 턴을했고 그는을 선택합니다 w[2] = 0. 마지막 턴에서 Alice는를 선택합니다 w[3] = 1. 결과 단어 0001는에서 발견 G되므로 Alice가 게임에서 승리합니다.

밥이를 선택했다면 w[2] = 1, 앨리스는 w[3] = 0마지막 턴에서 선택할 수 있었고 여전히 이길 수있었습니다. 이것은 Bob이 어떻게 플레이하든 Alice가 게임에서 이길 수 있음을 의미합니다. 이 상황에서 Alice는 승리 전략을 가지고 있습니다. 이 전략은 Bob의 차례에 해당하는 수준으로 분기되고 모든 분기에 다음 단어가 포함 된 레이블이 지정된 이진 트리로 시각화 할 수 있습니다 G.

A A B A

-0-0-0-1
    \
     1-0

앨리스는 단순히 자신의 차례에 가지를 따라 연주합니다. Bob이 어떤 지점을 선택하든 Alice는 결국 승리합니다.

입력

당신은 길이로 입력으로 제공 nG 가 문자열 인 (비어있는) 목록으로 설정 n합니다.

산출

당신의 결과는 Alice가 승리 전략을 가지고있는 턴 순서의 목록이며, 이것은 위에서 설명한 이진 트리의 존재와 같습니다. 턴 주문의 순서는 중요하지 않지만 중복은 금지되어 있습니다.

세부 규칙

전체 프로그램 또는 함수를 작성할 수 있습니다. 프로그램의 경우 입력 및 출력에 대한 구분 기호를 선택할 수 있지만 둘 다 동일해야합니다. 가장 짧은 바이트 수가 이기고 표준 허점은 허용되지 않습니다.

테스트 사례

3 [] -> []
3 [000,001,010,011,100,101,110,111] -> [AAA,AAB,ABA,ABB,BAA,BAB,BBA,BBB]
4 [0001,1011,0010] -> [AAAA,BAAA,AABA]
4 [0001,1011,0010,0110,1111,0000] -> [AAAA,BAAA,ABAA,BBAA,AABA,AAAB]
5 [00011,00110,00111,11110,00001,11101,10101,01010,00010] -> [AAAAA,BAAAA,ABAAA,BBAAA,AABAA,AAABA,BAABA,AAAAB,AABAB]

재미있는 사실

출력에서 턴 순서의 수는 항상 목표 세트의 단어 수와 같습니다.



답변

Dyalog APL, 59 바이트

{(a≡,⊂⍬)∨0=⍴a←∪⍵:a⋄(∇h/t)(('A',¨∪),'B',¨∩)∇(~h←⊃¨a)/t←1↓¨a}

@xnor의 솔루션과 동일한 알고리즘.

(a≡,⊂⍬)∨0=⍴a←∪⍵:a
           a←∪⍵    ⍝ "a" is the unique items of the argument
        0=⍴a       ⍝ is it empty?
 a≡,⊂⍬             ⍝ is it a vector that contains the empty vector?
       ∨       :a  ⍝ if any of the above, return "a"

(∇h/t)(('A',¨∪),'B',¨∩)∇(~h←⊃¨a)/t←1↓¨a
                                 t←1↓¨a  ⍝ drop an item from each of "a" and call that "t"
                         ~h←⊃¨a          ⍝ first of each of "a", call that "h", then negate it
                                /        ⍝ use "~h" as a boolean mask to select from "t"
                       ∇                 ⍝ apply a recursive call
(∇h/t)                                   ⍝ use "h" as a boolean mask on "t", then a recursive call
      (('A',¨∪),'B',¨∩)                  ⍝ apply a fork on the results from the two recursive calls:
       ('A',¨∪)                          ⍝   prepend 'A' to each of the intersection
               ,                         ⍝   concatenated with
                'B',¨∪                   ⍝   prepend 'B' to each of the union

답변

파이썬, 132

def f(S,n):
 if n<1:return S
 a,b=[f({x[1:]for x in S if x[0]==c},n-1)for c in'01']
 return{'A'+y for y in a|b}|{'B'+y for y in a&b}

예제 실행 :

f({'000','001','010','011','100','101','110','111'},3) ==
{'ABA', 'ABB', 'AAA', 'AAB', 'BBB', 'BBA', 'BAB', 'BAA'}

이것은 단지 알고리즘을 보여주기 위해 일종의 골프입니다. 입력과 출력은 문자열 세트입니다. 파이썬은 이것의 일부를 콤팩트하게 표현할 수있는 올바른 기능을 가지고 있지 않은 것 같습니다. 따라서 누군가가 더 적합한 언어로 이것을 작성하면 멋질 것입니다.

재귀를 수학적으로 표현하는 방법은 다음과 같습니다. 불행히도 PPCG에는 여전히 수학 렌더링이 부족하므로 코드 블록을 사용해야합니다.

관심 객체는 문자열 세트입니다. |집합 노조를 대표 하자& 세트 교차로를 나타냅니다.

c문자 인 경우의 모든 문자열 c#S앞에 문자 c를 추가하십시오 S. 반대로 수축 은 초기 문자 뒤에 c\S오는 한 문자 길이의 짧은 문자열이 S되도록하십시오 ( c예 🙂 0\{001,010,110,111} = {01,10}.

첫 문자 S로 문자 세트로 문자열 세트를 고유하게 나눌 수 있습니다 01.

S = 0#(0\S) | 1#(1\S)

그런 다음 f첫 두 줄의 기본 사례와 마지막 줄의 재귀 캔을 사용하여 원하는 기능 을 다음과 같이 표현할 수 있습니다.

f({})   = {}
f({''}) = {''}
f(S)    = A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S))

length를 사용할 필요는 없습니다 n.

왜 이것이 작동합니까? Alice가 문자열 집합에서 이길 수있는 이동 문자열에 대해 생각해 봅시다 S.

첫 번째 캐릭터가 A이면 Alice는 첫 번째 이동 ( ‘0’또는 ‘1’)을 선택하여 문제를 S0또는 로 줄일 수 있습니다 S1. 따라서 이제 남은 이동 스트링은 적어도 하나 f(S0)또는 에 있어야 f(S1)하므로 조합을 취합니다 |.

마찬가지로 첫 번째 문자가 ‘B’이면 Bob이 선택하고 Alice는 더 나쁜 문자를 선택하므로 나머지 이동 문자열은 교차점 ( &) 에 있어야합니다 .

기본 사례는 단순히 S비어 있는지 여부를 확인합니다 . 우리가 문자열의 길이를 추적한다면, 우리가 n되풀이 할 때마다 1을 빼서 대신에 밑을 쓸 수 있습니다 :

f(S) = S if n==0

재귀 솔루션은와 f(S)같은 크기 의 재미있는 사실을 설명합니다 S. 기본 사례와 유도 사례의 경우에도 마찬가지입니다.

f(S) = A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S))

우리는

size(f(S)) = size(A#(f(0\S)|f(1\S)) | B#(f(0\S)&f(1\S)))
           = size(A#(f(0\S)|f(1\S))) + size(B#(f(0\S)&f(1\S))))
           = size((f(0\S)|f(1\S))) + size((f(0\S)&f(1\S))))
           = size(f(0\S)) + size(f(1\S))  [since size(X|Y) + size(X&Y) = size(X) + size(Y)]
           = size(0\S) + size(1\S)
           = size(S)