Chiến lược chiến thắng cho một trò chơi xây dựng chuỗi


14

Lý lịch

Alice và Bob chơi một trò chơi gọi là xây dựng một từ nhị phân . Để chơi trò chơi, bạn sửa một độ dài n >= 0, một tập hợp Gcác ntừ nhị phân có độ dài được gọi là bộ mục tiêu và một nchuỗi dài tchứa các chữ cái AB, được gọi là thứ tự lần lượt . Trò chơi kéo dài theo nlượt và đến lượt i, người chơi được xác định bằng cách t[i]chọn một chút w[i]. Khi trò chơi kết thúc, người chơi nhìn vào từ nhị phân wmà họ đã xây dựng. Nếu từ này được tìm thấy trong bộ mục tiêu G, Alice thắng trò chơi; nếu không, Bob thắng.

Ví dụ, chúng ta hãy sửa chữa n = 4, G = [0001,1011,0010]t = AABA. Alice đến lượt đầu tiên, và cô chọn w[0] = 0. Lượt thứ hai cũng là của Alice và cô ấy chọn w[1] = 0. Bob có lượt thứ ba, và anh ta chọn w[2] = 0. Ở lượt cuối cùng, Alice chọn w[3] = 1. Từ kết quả 0001, được tìm thấy trong G, vì vậy Alice thắng trò chơi.

Bây giờ, nếu Bob đã chọn w[2] = 1, Alice có thể đã chọn w[3] = 0trong lượt cuối cùng của mình, và vẫn chiến thắng. Điều này có nghĩa là Alice có thể giành chiến thắng trong trò chơi cho dù Bob chơi như thế nào. Trong tình huống này, Alice có một chiến lược chiến thắng . Chiến lược này có thể được hình dung như một cây nhị phân có nhãn, phân nhánh ở các cấp tương ứng với lượt của Bob và mỗi nhánh có một từ từ G:

A A B A

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

Alice chơi bằng cách đơn giản là đi theo các nhánh trên lượt của mình; cho dù Bob chọn ngành nào, Alice cuối cùng cũng thắng.

Đầu vào

Bạn được cung cấp dưới dạng đầu vào độ dài nvà tập hợp Gdưới dạng danh sách các chuỗi có độ dài (có thể trống) n.

Đầu ra

Đầu ra của bạn là danh sách các đơn hàng lần lượt mà Alice có chiến lược chiến thắng, tương đương với sự tồn tại của cây nhị phân như mô tả ở trên. Thứ tự của các đơn đặt hàng lần lượt không quan trọng, nhưng trùng lặp bị cấm.

Quy tắc chi tiết

Bạn có thể viết một chương trình đầy đủ hoặc một chức năng. Trong trường hợp của một chương trình, bạn có thể chọn dấu phân cách cho đầu vào và đầu ra, nhưng nó phải giống nhau cho cả hai. Số byte ngắn nhất sẽ thắng và các sơ hở tiêu chuẩn không được phép.

Các trường hợp thử nghiệm

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]

Sự thật thú vị

Số lượng lệnh lần lượt trong đầu ra luôn bằng số lượng từ trong mục tiêu được đặt.


5
Tôi khá bị thu hút bởi thực tế là đầu vào và đầu ra có kích thước bằng nhau. Bạn có một bằng chứng hoặc trích dẫn cho thực tế này? Tôi tự hỏi nếu có một cách để tính toán chức năng này mà duy trì trực quan kích thước.
xnor

2
Trường hợp thử nghiệm số 5 của bạn mâu thuẫn với sự thật thú vị của bạn ...
mbomb007

3
@ mbomb007 Trường hợp thử nghiệm # 5 liệt kê 11101hai lần; thực tế thú vị vẫn giữ cho bộ. Zgarb, đầu vào có thể chứa các yếu tố lặp đi lặp lại, hoặc đây có phải là lỗi không?
xnor

@xnor Đây là một cái gì đó xuất hiện trong nghiên cứu của tôi một thời gian trước đây. Tôi có một bằng chứng trong bản in lại này , trang 16, nhưng về cơ bản nó giống như của bạn.
Zgarb

1
@xnor Theo trực giác, ở bất kỳ lượt nào, nếu cả 0 và 1 đều là lựa chọn chiến thắng, thì Alice hoặc Bob có thể chọn nước đi tiếp theo. Nếu chỉ có một lựa chọn chiến thắng thì Alice phải chọn tiếp theo. Do đó, số lượng lựa chọn cho chuỗi giống như số lượng lựa chọn của chiến lược chiến thắng. Khó khăn nghiêm ngặt, nhưng hấp dẫn.
Alchymist

Câu trả lời:


1

APL Dyalog, 59 byte

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

Thuật toán tương tự như trong giải pháp củ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

13

Con trăn, 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}

Chạy ví dụ:

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

Đây chỉ là loại golf, chủ yếu để hiển thị thuật toán. Đầu vào và đầu ra là tập hợp các chuỗi. Python dường như không có các tính năng phù hợp để diễn đạt các phần của điều này một cách gọn gàng, vì vậy sẽ thật tuyệt nếu ai đó viết nó bằng ngôn ngữ phù hợp hơn.

Đây là cách đệ quy có thể được thể hiện bằng toán học. Thật không may, PPCG vẫn thiếu kết xuất toán học, vì vậy tôi sẽ phải sử dụng các khối mã.

Các đối tượng quan tâm là bộ chuỗi. Hãy |biểu diễn tập hợp và &biểu diễn tập hợp giao nhau.

Nếu clà một ký tự, hãy c#Sbiểu diễn chuẩn bị ký tự ccho tất cả các chuỗi trong S. Ngược lại, hãy để sự co lại c\Slà các chuỗi ngắn hơn một ký tự của Ský tự ban đầu c, ví dụ : 0\{001,010,110,111} = {01,10}.

Chúng ta có thể phân chia duy nhất một tập hợp các chuỗi Svới ký tự trong ký tự 01đầu tiên của chúng.

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

Sau đó, chúng ta có thể biểu thị hàm mong muốn fnhư sau, với các trường hợp cơ sở trong hai dòng đầu tiên và có thể đệ quy trong dòng cuối cùng:

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

Lưu ý rằng chúng ta không cần sử dụng chiều dài n.

Tại sao điều này làm việc? Chúng ta hãy nghĩ về các chuỗi di chuyển cho phép Alice giành chiến thắng cho một chuỗi các chuỗi S.

Nếu ký tự đầu tiên là A, Alice có thể chọn nước đi đầu tiên ('0' hoặc '1'), để cô ấy chọn giảm vấn đề xuống S0hoặc S1. Vì vậy, bây giờ chuỗi di chuyển còn lại phải có ít nhất một f(S0)hoặc f(S1), do đó chúng tôi có liên minh của họ |.

Tương tự, nếu ký tự đầu tiên là 'B', Bob sẽ chọn và anh ta sẽ chọn nhân vật xấu hơn cho Alice, do đó, chuỗi di chuyển còn lại phải nằm trong giao lộ ( &).

Các trường hợp cơ bản chỉ cần kiểm tra nếu Strống hoặc không ở cuối. Nếu chúng tôi theo dõi độ dài của chuỗi n, bằng cách trừ 1 mỗi lần chúng tôi lặp lại, các cơ sở có thể được viết:

f(S) = S if n==0

Giải pháp đệ quy cũng giải thích thực tế thú vị f(S)có cùng kích thước S. Nó đúng với các trường hợp cơ sở và cho trường hợp quy nạp

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

chúng ta có

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)

Chạy mã của bạn cho TypeError: 'int' object is not subscriptable. Bạn có một liên kết đến một chương trình runnable? Tôi vừa dán nó và chạy nó vớiprint f([0001,1011,0010],4)
mbomb007

@ mbomb007 Hàm cần được gọi như thế nào f({'000','001','010','011','100','101','110','111'},3). Bạn có nhận được một lỗi theo cách này?
xnor

Ah, tôi đã không thấy tôi bị thiếu dấu ngoặc kép, cảm ơn. Nó cũng chạy vớiprint f(['0001','1011','0010'],4)
mbomb007

Nếu bạn muốn chạy chương trình biết nđộc lập với các tham số thì nó sẽ làn=len(S[0])if S!=[]else 0
mbomb007

Chạy nó ở đây: repl.it/7yI
mbomb007
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.