用 13 个双端队列模拟牌堆:放到堆顶用 push_front、抽取堆底用 pop_back,每翻开一张就累加点数计数,最后数 A~Q 里凑满 4 张正面朝上的点数。
OJ: roj
题目 ID: 3022
难度:普及-
标签:模拟队列python
创建: 2026-10-01 10:37
更新: 2026-10-07 13:50
题目描述
一副去掉大小王的扑克共 52 张均分成 13 堆编号 0=10、0 代表 10);输出一个整数表示开出的总对数。样例输入 13 行依次为 8 5 A A、K 5 3 2、9 6 0 6、3 4 3 4、3 4 4 5、5 6 7 6、8 7 7 7、9 9 8 8、9 0 0 0、K J J J、Q A Q K、J Q 2 2、A K Q 2,样例输出 9。
思路
照规则模拟:13 个牌堆各用一个双端队列,「压到最上面」是队首插入、「从最下面取」是队尾弹出,两端不能写反。由于点数为 up[v] 记录点数 up[v]==4 的
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-01 10:37
# update_at: 2026-10-01 10:42
import sys
from collections import deque
# 牌面 → 点数:A=1、2~9 原值、0 代表 10、J=11、Q=12、K=13(按这张表逐字加一)。
# 键用 bytes,直接匹配 sys.stdin.buffer 切出来的牌面,省一次解码。
VALUE = {name.encode(): value for value, name in enumerate("A234567890JQK", 1)}
PILE_COUNT = 13 # 13 堆,每堆 4 张
LIVES = 4 # 4 张 K = 4 条命
DEATH = 13 # K 的点数;编号 13 的堆既是生命牌堆,也是点数 13 对应的堆
def solve() -> None:
data = iter(sys.stdin.buffer.read().split())
piles: list[deque[int]] = [deque() for _ in range(PILE_COUNT + 1)]
for i in range(PILE_COUNT): # 输入顺序即从上到下
pile_size = 4
piles[i + 1].extend(VALUE[next(data)] for _ in range(pile_size))
up = [0] * (PILE_COUNT + 1) # up[v]:点数 v 已经"翻开并入堆"了多少张
for _ in range(LIVES): # 每死一条命就回到第 1 步,重抽生命牌堆顶
value = piles[DEATH].popleft() # 第 1 步:抽生命牌堆最上面的一张
while value != DEATH: # 抽到 K 则本轮结束,K 被扔掉、不再入堆
piles[value].appendleft(value) # 正面朝上压到对应编号堆的最上边
up[value] += 1
value = piles[value].pop() # 再从同一堆最底下抽一张,继续往下走
print(sum(up[v] == 4 for v in range(1, DEATH))) # A~Q 中凑齐 4 张正面朝上的点数个数
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 11:13
* update_at: 2026-10-06 11:13
*/
#include <deque>
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
deque<ll> piles[14]; // piles[i]:第 i 堆牌,从上到下存点数(队首是堆顶)
ll up[14]; // up[v]:点数 v 被翻开并压入 v 号堆的次数,即正面朝上的 v 的张数
// 把牌面转成点数:A=1、2~9 为原值、0=10、J=11、Q=12、K=13
ll face_to_value(const string &face) {
if (face == "A") return 1;
if (face == "0") return 10;
if (face == "J") return 11;
if (face == "Q") return 12;
if (face == "K") return 13;
return face[0] - '0';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 读入 13 行,每行 4 张牌,行内顺序即从上到下
for (ll i = 1; i <= 13; i++) {
for (ll j = 1; j <= 4; j++) {
string face;
cin >> face;
piles[i].push_back(face_to_value(face));
}
}
// 4 条命:每条命先抽生命牌堆顶,再沿牌链走,直到抽到 K(点数 13)
for (ll life = 1; life <= 4; life++) {
ll value = piles[13].front(); // 生命牌堆最上面一张
piles[13].pop_front();
while (value != 13) {
piles[value].push_front(value); // 正面朝上压到 value 号堆顶
up[value]++;
ll next_value = piles[value].back(); // 从该堆最下面抽一张
piles[value].pop_back();
value = next_value;
}
}
ll answer = 0; // A~Q 中凑满 4 张正面朝上的点数个数(K 不计)
for (ll v = 1; v <= 12; v++) {
if (up[v] == 4) answer++;
}
cout << answer << '\n';
return 0;
}