Bỏ qua để đến nội dung

Vị trí thắng, vị trí thua

Mức độ: Cơ bản

Đề thi học sinh giỏi rất hay có bài hai người chơi: lần lượt bốc sỏi, đi quân cờ, viết số, ai không đi được nữa thì thua. Câu hỏi luôn là: nếu cả hai đều chơi giỏi nhất, người đi trước thắng hay người đi sau thắng? Bài này dạy cách trả lời bằng một ý rất đơn giản, rồi biến nó thành chương trình.

Có n viên sỏi. Hai bạn An (đi trước) và Bình lần lượt bốc 1, 2 hoặc 3 viên. Ai bốc viên cuối cùng thì thắng (nói cách khác: đến lượt mà không còn sỏi để bốc thì thua).

Gọi một số sỏi là vị trí thua nếu người phải đi ở vị trí đó chắc chắn thua (dù đi thế nào, đối thủ chơi giỏi vẫn thắng), và vị trí thắng nếu ngược lại. Có hai quy tắc:

  • Vị trí là thắng nếu có ít nhất một nước đi tới vị trí thua (đẩy đối thủ vào thế thua).
  • Vị trí là thua nếu mọi nước đi đều tới vị trí thắng (hoặc không có nước đi nào).

Bắt đầu từ 0 viên (không đi được: thua) rồi điền dần:

n012345678
Kết quảthuathắngthắngthắngthuathắngthắngthắngthua
  • n = 1, 2, 3: bốc hết, đối thủ còn 0 viên (thua). Vậy là thắng.
  • n = 4: bốc 1, 2, 3 thì còn 3, 2, 1, đều là vị trí thắng cho đối thủ. Vậy 4 là thua.
  • n = 5, 6, 7: bốc sao cho còn 4. Vậy là thắng.

Quy luật: n chia hết cho 4 thì người đi trước thua. Chiến lược của người thắng: luôn bốc sao cho số sỏi còn lại chia hết cho 4. Đối thủ bốc x viên thì mình bốc 4 - x viên.

Khi chưa đoán được quy luật, cứ làm đúng như bảng trên bằng chương trình: thang[i] cho biết vị trí i là thắng hay thua. Ví dụ mỗi lượt được bốc 1, 3 hoặc 4 viên:

def bang_thang_thua(n, nuoc_di):
thang = [False] * (n + 1) # thang[0] = False: không đi được thì thua
for i in range(1, n + 1):
thang[i] = any(i >= k and not thang[i - k] for k in nuoc_di)
return thang
thang = bang_thang_thua(14, [1, 3, 4])
print([i for i in range(15) if not thang[i]]) # [0, 2, 7, 9, 14]

Mỗi vị trí xét hết các nước đi, nên với n vị trí và k kiểu nước đi, chương trình làm khoảng n × k phép tính.

Với n tới 1018 thì không lập bảng tới n được. Nhìn bảng ở trên: các vị trí thua của trò chơi bốc 1, 3, 4 là 0, 2, 7, 9, 14, 16, … lặp lại sau mỗi 7 vị trí. Vậy chỉ cần xét n % 7: thua khi n % 7 bằng 0 hoặc 2.

Vì sao chắc chắn lặp lại? Kết quả ở vị trí i chỉ phụ thuộc vào kết quả của k vị trí ngay trước (k là số bốc nhiều nhất). Nếu một đoạn k kết quả liên tiếp xuất hiện lại, mọi thứ sau đó sẽ lặp lại y hệt. Có hữu hạn đoạn k kết quả (nhiều nhất 2k), nên chắc chắn sẽ có đoạn lặp. Cách làm trong thi: lập bảng vài trăm vị trí đầu, in ra để nhìn quy luật, rồi chứng minh hoặc kiểm tra quy luật đó bằng chương trình.

Có hai đống sỏi, mỗi lượt được bốc bao nhiêu viên cũng được từ một đống. Nếu hai đống bằng nhau thì người đi sau thắng: đối thủ bốc bao nhiêu ở đống nào, mình bốc đúng chừng đó ở đống kia. Hai đống luôn bằng nhau sau lượt của mình, nên mình không bao giờ là người hết nước đi trước.

Nếu hai đống khác nhau thì người đi trước bốc để hai đống bằng nhau, rồi chơi đối xứng. Ý “bắt chước đối thủ” giải được nhiều bài trông rất khó (xem phần luyện tập).

  • Đọc kỹ ai thắng: “ai lấy viên cuối thì thắng” khác hẳn “ai lấy viên cuối thì thua”. Với luật thứ hai, vị trí 0 viên lại là thắng (đối thủ vừa lấy viên cuối nên đối thủ thua), và cả bảng thay đổi.
  • “Chơi tối ưu” nghĩa là cả hai đều biết hết và đi nước tốt nhất. Kết quả chỉ phụ thuộc vị trí ban đầu, không phụ thuộc may rủi.

Khi có nhiều đống sỏi, mỗi lượt chọn một đống để chơi, thì cần công cụ mạnh hơn: trò chơi Nim và số Grundy (bài tiếp theo của chuyên đề).

Gồm 20 bài, chia thành 4 nhóm từ dễ đến khó. Mỗi bài có một dữ liệu kiểm tra: các bạn chạy chương trình của mình với dữ liệu đó rồi so với đáp số.

Có n viên sỏi, hai người lần lượt bốc, mỗi lượt bốc từ 1 đến k viên. Ai bốc viên cuối cùng thì thắng. Nhập n, k (1 ≤ n, k ≤ 1018). Nếu cả hai chơi tối ưu, in ra TRƯỚC nếu người đi trước thắng, SAU nếu người đi sau thắng.

Ví dụ: nhập 10 3 thì in ra TRƯỚC.

Kiểm tra: nhập 1000000 6.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Vẫn trò chơi ở bài 1. Nhập n, k (1 ≤ n, k ≤ 1018). Nếu người đi trước thắng, in ra số viên người đó phải bốc ở lượt đầu để chắc chắn thắng (nếu có nhiều cách, in số nhỏ nhất). Nếu người đi trước thua, in ra 0.

Ví dụ: nhập 10 3 thì in ra 2.

Kiểm tra: nhập 999983 10.

Xem đáp số

6

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n viên sỏi, mỗi lượt bốc từ 1 đến k viên, nhưng lần này ai bốc viên cuối cùng thì thua. Nhập n, k (1 ≤ n, k ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 5 3 thì in ra SAU.

Kiểm tra: nhập 1000001 6.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n viên sỏi, mỗi lượt phải bốc ít nhất a viên và nhiều nhất b viên. Nếu số sỏi còn lại ít hơn a thì người đến lượt không đi được và thua. Nhập n, a, b (1 ≤ a ≤ b ≤ 109, 0 ≤ n ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 6 2 3 thì in ra SAU.

Kiểm tra: nhập 1000000 3 7.

Xem đáp số

SAU

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n viên sỏi, mỗi lượt bốc đúng 1, 3 hoặc 4 viên. Ai không bốc được thì thua. Nhập n (0 ≤ n ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 7 thì in ra SAU.

Kiểm tra: nhập 1000000.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu là n, m (0 ≤ n ≤ 106, 1 ≤ m ≤ 10). Dòng thứ hai có m số nguyên dương khác nhau s1, …, sm (không quá 100): mỗi lượt được bốc đúng si viên với i nào đó. Ai không bốc được thì thua. In ra TRƯỚC hoặc SAU.

Ví dụ:

NhậpIn ra
14 3
1 3 4
SAU

Kiểm tra: nhập

1000000 4
2 5 7 11
Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Như bài 6, nhưng 0 ≤ n ≤ 1018 và các si không quá 20. In ra TRƯỚC hoặc SAU.

Ví dụ:

NhậpIn ra
1000000000000000000 3
1 3 4
TRƯỚC

Kiểm tra: nhập

123456789012345678 4
2 5 7 11
Xem đáp số

SAU

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n viên sỏi, mỗi lượt bốc một số viên là số chính phương (1, 4, 9, 16, …). Ai không bốc được thì thua. Nhập n (0 ≤ n ≤ 105). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 2 thì in ra SAU.

Kiểm tra: nhập 100000.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Trên bảng có số n. Mỗi lượt, người chơi chọn một ước d của số đang có, với 1 ≤ d nhỏ hơn số đó, rồi thay số đó bằng (số đó - d). Ai không đi được (khi số trên bảng là 1) thì thua. Nhập n (1 ≤ n ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 2 thì in ra TRƯỚC.

Kiểm tra: nhập 2026.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Dòng đầu là N, m (1 ≤ N ≤ 106, 1 ≤ m ≤ 10). Dòng thứ hai là tập nước đi s1, …, sm như bài 6. Có bao nhiêu số n từ 1 đến N mà người đi trước thua?

Ví dụ:

NhậpIn ra
14 3
1 3 4
4

Kiểm tra: nhập

1000000 3
2 3 7
Xem đáp số

400000

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bắt đầu với số p = 1. Hai người lần lượt nhân p với một số nguyên từ 2 đến 9. Người đầu tiên làm cho p ≥ n thì thắng. Nhập n (2 ≤ n ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 10 thì in ra SAU.

Kiểm tra: nhập 1000000000000000000.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Quân vua đứng ở ô (x, y) trên bàn cờ rất lớn (ô góc là (0, 0)). Mỗi lượt, người chơi di chuyển vua sang trái 1 ô, xuống dưới 1 ô, hoặc chéo xuống trái 1 ô (không ra ngoài bàn). Ai không đi được (vua ở (0, 0)) thì thua. Nhập x, y (0 ≤ x, y ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 2 2 thì in ra SAU.

Kiểm tra: nhập 1000 2026.

Xem đáp số

SAU

Hướng dẫn giải chi tiết: liên hệ tác giả.

n đồng xu xếp thành vòng tròn. Mỗi lượt, người chơi lấy đi 1 đồng xu, hoặc 2 đồng xu nằm cạnh nhau (hai đồng xu cạnh nhau ban đầu, ở giữa không có chỗ trống). Ai lấy đồng xu cuối cùng thì thắng. Nhập n (1 ≤ n ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 5 thì in ra SAU.

Kiểm tra: nhập 12.

Xem đáp số

SAU

Hướng dẫn giải chi tiết: liên hệ tác giả.

Trên bảng có số n. Mỗi lượt, người chơi chọn một chữ số khác 0 của số đang có và trừ số đó đi chữ số ấy. Ai làm số trên bảng thành 0 thì thắng. Nhập n (1 ≤ n ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 10 thì in ra SAU.

Kiểm tra: nhập 987654.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Thanh sô cô la có m × n ô. Mỗi lượt, người chơi chọn một miếng và bẻ nó dọc theo một đường kẻ thành hai miếng nhỏ hơn. Ai không bẻ được nữa (mọi miếng đều chỉ còn 1 ô) thì thua. Nhập m, n (1 ≤ m, n ≤ 109). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 2 3 thì in ra TRƯỚC.

Kiểm tra: nhập 999999999 1000000000.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

n đồng xu xếp thành hàng, đồng thứ i có giá trị ai. Hai người lần lượt lấy một đồng ở đầu trái hoặc đầu phải của hàng. Mỗi người muốn tổng giá trị mình lấy được lớn nhất. Dòng đầu là n (1 ≤ n ≤ 1000), dòng sau là a1, …, an (không quá 109). In ra tổng giá trị người đi trước lấy được khi cả hai chơi tối ưu.

Ví dụ:

NhậpIn ra
4
3 9 1 2
11

Kiểm tra: nhập

8
5 3 7 10 8 1 6 2
Xem đáp số

26

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có hai đống sỏi a viên và b viên. Mỗi lượt, người chơi chọn một đống và bốc bao nhiêu viên cũng được (ít nhất 1). Ai bốc viên cuối cùng thì thắng. Nhập a, b (0 ≤ a, b ≤ 1018). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 3 3 thì in ra SAU.

Kiểm tra: nhập 26 25.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Đồ thị có hướng không có chu trình, n đỉnh đánh số 1 đến n và m cạnh. Một quân cờ đứng ở đỉnh s. Mỗi lượt, người chơi di chuyển quân theo một cạnh có hướng xuất phát từ đỉnh đang đứng. Ai không đi được thì thua. Dòng đầu là n, m, s (1 ≤ n ≤ 105, 0 ≤ m ≤ 2 × 105). m dòng sau, mỗi dòng u v là cạnh từ u đến v. In ra TRƯỚC hoặc SAU.

Ví dụ:

NhậpIn ra
4 4 1
1 2
1 3
2 4
3 4
SAU

Kiểm tra: nhập

6 7 1
1 2
1 3
2 4
3 5
4 6
5 6
3 4
Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Có n viên sỏi, mỗi lượt bốc từ 1 đến k viên, nhưng không được bốc đúng bằng số viên đối thủ vừa bốc ở lượt ngay trước (lượt đầu tiên thì tự do). Ai không đi được thì thua. Nhập n, k (1 ≤ n ≤ 2000, 1 ≤ k ≤ 20). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 4 3 thì in ra SAU.

Kiểm tra: nhập 2000 20.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.

Bàn cờ N × N, các ô đánh số (x, y) với 1 ≤ x, y ≤ N. Quân mã đứng ở ô (x, y). Mỗi lượt chỉ được đi một trong bốn nước: (x - 2, y + 1), (x - 2, y - 1), (x + 1, y - 2), (x - 1, y - 2), và phải ở trong bàn. Ai không đi được thì thua. Nhập N, x, y (1 ≤ x, y ≤ N ≤ 1000). In ra TRƯỚC hoặc SAU.

Ví dụ: nhập 15 5 4 thì in ra TRƯỚC.

Kiểm tra: nhập 1000 777 999.

Xem đáp số

TRƯỚC

Hướng dẫn giải chi tiết: liên hệ tác giả.