熱門文章

2026年3月5日 星期四

ZeroJudge 解題筆記:c103. 00131 - The Psychic Poker Player

作者:王一哲
日期:2026年3月5日


ZeroJudge 題目連結:c103. 00131 - The Psychic Poker Player

解題想法


這題我只寫了 Python 版本。我先為每一種牌型寫一個自訂函式,再用另一個自訂函式 check 檢查手牌的牌型,由高分檢查到低分。最後再寫一個自訂函式 check 測試各種換牌張數的最高分。討論區裡有一種用數學計算的寫法,但我沒有認真推導數學關係式,就沒有寫了。

Python 程式碼


使用時間約為 10 ms,記憶體約為 3.4 MB,通過測試。
import sys, itertools

def flush(hand):  # 手牌是否為同花
    return all(card[1] == hand[0][1] for card in hand[1:])

def straight(hand):  # 手牌是否為順子
    nums = {'A': 14, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
            '8': 8, '9':9, 'T': 10, 'J': 11, 'Q': 12, 'K': 13}  # 牌面數字
    flag1, flag2 = True, True  # 是否為 A, 2, 3, 4, 5,是否為其它號碼的順子
    if hand[-1][0] == 'A':  # 特例,最大的是 A,要檢查是否為 A, 2, 3, 4, 5
        pre = 1  # A 當作 1
        for card in hand[:-1]:
            curr = nums[card[0]]
            if curr != pre+1:
                flag1 = False
                break
            pre = curr
    else: flag1 = False  # 最大的不是 A,不可能是 A, 2, 3, 4, 5
    # 其它號碼的順子
    pre = nums[hand[0][0]]
    for card in hand[1:]:
        curr = nums[card[0]]
        if curr != pre+1:
            flag2 = False
            break
        pre = curr
    return flag1 or flag2

def straight_flush(hand):  # 手牌是否為同花順
    return flush(hand) and straight(hand)

def four_of_a_kind(hand):  # 手牌是否為鐵支
    cnt = dict()  # 計數器
    for card in hand:  # 依序讀取手牌數值
        n = card[0]
        if n not in cnt: cnt[n] = 1
        else: cnt[n] += 1
    return max(cnt.values()) == 4  # 如果某一種數值有 4 張,是鐵支

def full_house(hand):  # 手牌是否為葫蘆
    cnt = dict()  # 計數器
    for card in hand:  # 依序讀取手牌數值
        n = card[0]
        if n not in cnt: cnt[n] = 1
        else: cnt[n] += 1
    tmp = sorted(cnt.values())  # 牌面數量排序
    return len(tmp) == 2 and tmp[0] == 2 and tmp[1] == 3

def three_of_a_kind(hand):  # 手牌是否為三條
    cnt = dict()  # 計數器
    for card in hand:  # 依序讀取手牌數值
        n = card[0]
        if n not in cnt: cnt[n] = 1
        else: cnt[n] += 1
    tmp = sorted(cnt.values())  # 牌面數量排序
    return len(tmp) == 3 and tmp[0] == 1 and tmp[1] == 1 and tmp[2] == 3

def two_pairs(hand):  # 手牌是否為兩對
    cnt = dict()  # 計數器
    for card in hand:  # 依序讀取手牌數值
        n = card[0]
        if n not in cnt: cnt[n] = 1
        else: cnt[n] += 1
    tmp = sorted(cnt.values())  # 牌面數量排序
    return len(tmp) == 3 and tmp[0] == 1 and tmp[1] == 2 and tmp[2] == 2

def one_pair(hand):  # 手牌是否為一對
    cnt = dict()  # 計數器
    for card in hand:  # 依序讀取手牌數值
        n = card[0]
        if n not in cnt: cnt[n] = 1
        else: cnt[n] += 1
    tmp = sorted(cnt.values())  # 牌面數量排序
    return len(tmp) == 4 and tmp[0] == 1 and tmp[1] == 1 and tmp[2] == 1 and tmp[3] == 2

def check(hand):
    if straight_flush(hand): return 8
    if four_of_a_kind(hand): return 7
    if full_house(hand): return 6
    if flush(hand): return 5
    if straight(hand): return 4
    if three_of_a_kind(hand): return 3
    if two_pairs(hand): return 2
    if one_pair(hand): return 1
    return 0

def change(hand, deck):  # 輸入手牌、牌庫,依序檢查
    result = [[0, i] for i in range(6)]  # 從牌庫換 0 ~ 5 張牌可組成的最大組合,[組合, 換牌數量]
    result[0][0] = check(hand)
    # 換一張牌,測試換第一張 ~ 第五張的效果
    best = 0
    for i in range(5):
        tmp = hand[:i] + hand[i+1:] + [deck[0]]
        tmp.sort(key = lambda x : (nums[x[0]], shdc[x[1]]))
        best = max(best, check(tmp))
    result[1][0] = best
    # 換二張牌,測試換其中兩張牌的效果
    best = 0
    for i, j in itertools.combinations(range(5), 2):
        tmp = hand.copy()
        tmp.remove(hand[i])
        tmp.remove(hand[j])
        tmp += deck[:2]
        tmp.sort(key = lambda x : (nums[x[0]], shdc[x[1]]))
        best = max(best, check(tmp))
    result[2][0] = best
    # 換三張牌,測試換其中三張牌的效果
    best = 0
    for i, j, k in itertools.combinations(range(5), 3):
        tmp = hand.copy()
        tmp.remove(hand[i])
        tmp.remove(hand[j])
        tmp.remove(hand[k])
        tmp += deck[:3]
        tmp.sort(key = lambda x : (nums[x[0]], shdc[x[1]]))
        best = max(best, check(tmp))
    result[3][0] = best
    # 換四張牌,測試只留其中一張牌的效果
    best = 0
    for i in range(5):
        tmp = [hand[i]] + deck[:4]
        tmp.sort(key = lambda x : (nums[x[0]], shdc[x[1]]))
        best = max(best, check(tmp))
    result[4][0] = best
    # 全換
    result[5][0] = check(deck)
    result.sort(reverse=True)
    return result[0]

for line in sys.stdin:
    hand = line.split()[:5]
    deck = line.split()[5:]
    nums = {'A': 14, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
            '8': 8, '9':9, 'T': 10, 'J': 11, 'Q': 12, 'K': 13}  # 牌面數字
    shdc = {'S': 0, 'H': 1, 'D': 2, 'C': 3}  # 花色
    comb = ("highest-card", "one-pair", "two-pairs", "three-of-a-kind", "straight",
            "flush", "full-house", "four-of-a-kind", "straight-flush")
    sorted_hand = sorted(hand, key = lambda x : (nums[x[0]], shdc[x[1]]))  # 數字、花色由小到大排序
    best_comb, change_cards = change(sorted_hand, deck)
    print("Hand: ", end="")
    print(*hand, end=" ")
    print("Deck: ", end="")
    print(*deck, end=" ")
    print(f"Best hand: {comb[best_comb]:s}")


沒有留言:

張貼留言