想学学「模拟」算法吗?【CSP-J 2024】扑克牌详细解析!
本帖最后由 高山 于 2026-9-23 05:55 编辑如果你想学习学习「模拟」算法 或正在复习CSP第二轮
那你可以按照本系列的节奏进行
在本系列中,我们将带领鱼油一同回顾算法基础,完成CSP算法真题,帮助他们尽可能在复赛获得更高的分数。
话不多说,让我们先看看题目:可使用配套的在线OJ评测本题->
static/image/hrline/line1.png
P11227 扑克牌
小 P 从同学小 Q 那儿借来一副 n 张牌的扑克牌。
本题中我们不考虑大小王,此时每张牌具有两个属性:花色和点数。
[*]花色共有 4 种:方片、草花、红桃、黑桃。
[*]点数共有 13 种,从小到大分别为 A、2、3、4、5、6、7、8、9、T、J、Q、K。
注意:点数 10 在本题中记为 T。
【完整的一副牌】
我们称一副扑克牌是完整的,当且仅当对于每一种花色和每一种点数,都恰好有一张牌具有对应的花色和点数。
由此,一副完整的扑克牌恰好有 4 × 13 = 52 张牌。
如图:
【题意】
小 P 借来的牌可能不是完整的,为此小 P 准备再向同学小 S 借若干张牌。可以认为小 S 每种牌都有无限张,因此小 P 可以任意选择借来的牌。
小 P 想知道他至少得向小 S 借多少张牌,才能让从小 S 和小 Q 借来的牌中,可以选出 52 张牌构成一副完整的扑克牌。
【输入格式】
输入的第一行包含一个整数 n,表示牌数。
接下来 n 行:每行包含一个长度为 2 的字符串描述一张牌,其中第一个字符描述其花色,第二个字符描述其点数。
例如 CA 表示草花 A,ST 表示黑桃 T(黑桃 10)。
【输出格式】
输出一行一个整数,表示最少还需要向小 S 借几张牌才能凑成一副完整的扑克牌。
【输入输出样例】
输入 #1
1
SA
输出 #1
51
输入 #2
4
DQ
DQ
DT
H3
输出 #2
49
输入 #3
52
DA DK D2 D3 D4 D5 D6 D7 D8 D9 DT DJ DQ C2 C3 C4 C5 C6 C7 C8 C9 CT CJ CQ CK HA HK H2 H3 H4 H5 H6 H7 H8 H9 HT HJ HQ S2 S3 S4 S5 S6 S7 S8 S9 ST SJ SQ SK
输出 #3
0
【样例解释】
样例 1:这一副牌中包含一张黑桃 A,小 P 还需要借除了黑桃 A 以外的 51 张牌以构成一副完整的扑克牌。
样例 2:这一副牌中包含两张方片 Q、一张方片 T(方片 10)以及一张红桃 3,小 P 还需要借除了红桃 3、方片 T 和方片 Q 以外的 49 张牌。
样例 3:这一副扑克牌是完整的,故不需要再借任何牌。该样例满足所有牌按照点数从小到大依次输入,点数相同时按照方片、草花、红桃、黑桃的顺序依次输入。
【数据范围】
对于所有测试数据,保证:
条件范围
牌数 n1 ≤ n ≤ 52
字符串长度2
首字符D、C、H、S 之一
第二个字符A 2 3 4 5 6 7 8 9 T J Q K 之一
【特殊性质】
特殊性质 A:保证输入的 n 张牌两两不同。
特殊性质 B:保证所有牌按照点数从小到大依次输入,点数相同时按照方片、草花、红桃、黑桃的顺序依次输入。
性质条件
特殊性质 A输入的 n 张牌两两不同
特殊性质 B按点数从小到大输入,同点数按 D、C、H、S 顺序
这题怎么做?
如果你正在尝试完成本题目,但又遇到了一些困扰,你可以根据本次的提示「一步步来」,不要担心哟~
如果你已经完成,想要查看答案,请直接点击「下一页」忽略本页提示即可~
对题目有问题,请不要吝啬「评论」本帖或在C/C++版块进行求助哦~!我们将对提出问题的童鞋进行评分奖励!
先看一张图:
接下来是提示(部分由AI生成)
凑牌思路 · 四步引导
第 1 步:把"借牌"翻译成一句话
核心问题:一副完整牌有 52 个"坑位"(4 花色 × 13 点数),你手上占了几个坑位?没占的就是要借的。
你有的牌含义
第一张占 1 个坑填上对应的那个花色+点数
重复的牌(如两张方片Q)第二个是废牌,坑位已被占,不再增加
没出现过的坑位空着,得借一张来填
一句话:借牌数 = 52 − 你占住的坑位数。
第 2 步:用什么"盒子"记坑位?
52 个坑位,用一个大小为 52 的数组(或二维数组 4×13)来标记"有没有"。
表示方式写法
一维数组bool have; 编号 = 花色×13 + 点数
二维数组bool have; 花色一行、点数一列
提示:两种都行。一维的用"花色×13+点数"把两个属性压成一个编号;二维的更直观,对应上图的表格。
第 3 步:字符怎么变成编号?
输入是两个字符:花色 c、点数 c。需要把它们分别映射成 0~3 和 0~12。
花色DCHS
编号0123
点数A23…TJQK
编号012…9101112
提醒:点数不能直接用 c-'A',因为 T 在字母表里位置很靠后、和 A 之间不连续。用一串 if-else 或写一个映射函数最稳妥。
第 4 步:处理完 n 张牌后怎么算答案?
操作代码思路
读入一张牌cin >> s; 取 s 和 s
算编号int box = suit(s) * 13 + rank(s);
标记坑位have = true; (重复来也只标一次)
统计已有遍历 have,数 true 的个数 cnt
输出答案cout << 52 - cnt;
自检清单(交之前过一遍):
□ 数组开 52(或 4×13),且初始化为 false
□ 花色、点数的映射覆盖全部 4+13 种情况
□ 循环统计时是 0..51,不是 0..n
□ 输出的是 52−cnt,不是 cnt
□ 关同步 ios::sync_with_stdio(false); cin.tie(0);
这里就提示一个「一维数组」的解决方案:
会了吗?不会也没关系~可以@FISHC AI继续提问哦~
static/image/hrline/line1.png
标准程序:
**** Hidden Message *****
测试点数据:
**** Hidden Message *****
如果喜欢,不要忘记「评分」哟~!
本帖最后由 zhangjinxuan 于 2026-9-22 21:38 编辑
讲的很棒{:10_275:}这里提供一个基于 std::set 的做法,用于拓展学习。
第一步转化为 52 - 输入数据中不同牌的个数,因此将所有输入的卡牌加入 std::set 中,根据 std::set 的特性,重复的卡牌不会被插入,最终集合的大小就是不同卡牌的个数。
该算法时间复杂度为 $O(n \log n)$,比原做法慢,但其有极强的通用性,可以支持多种复杂的卡牌类型。
// ...
int main() {
set<string> s;
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
string card;
cin >> card;
s.insert(card);
}
cout << 52 - s.size() << "\n";
return 0;
}
思路
这题本质就是“去重计数”。
一副完整牌有 4 × 13 = 52 个位置。
你手里每出现一张不同的牌,就占掉一个位置;重复的牌不会多占位置。
所以答案就是:
52 - 已经出现过的不同牌数
步骤
1. 开一个 bool have,表示每个花色、每个点数有没有出现过。
2. 把花色 D、C、H、S 映射成 0、1、2、3。
3. 把点数 A、2、3、…、T、J、Q、K 映射成 0 到 12。
注意:点数 T 表示 10,不能直接用 c - 'A',因为字母不连续。
4. 每读入一张牌,就把对应位置标记成 true。
5. 最后统计 have 里有多少个 true,输出 52 - cnt。
参考代码
#include <bits/stdc++.h>
using namespace std;
int getSuit(char c) {
if (c == 'D') return 0;
if (c == 'C') return 1;
if (c == 'H') return 2;
return 3; // S
}
int getRank(char c) {
string r = "A23456789TJQK";
for (int i = 0; i < 13; i++) {
if (r == c) return i;
}
return -1;
}
int main() {
int n;
cin >> n;
bool have = {false};
for (int i = 0; i < n; i++) {
string s;
cin >> s;
int suit = getSuit(s);
int rank = getRank(s);
have = true;
}
int cnt = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 13; j++) {
if (have) cnt++;
}
}
cout << 52 - cnt << endl;
return 0;
}
注意
重复的牌只算一次,比如两张方片 Q,只会占一个位置。
所以样例 2 中实际占的位置是:红桃 3、方片 T、方片 Q,共 3 个,答案是 52 - 3 = 49。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 {:7_146:} {:7_146:} {:10_269:} {:10_266:} 看看
{:5_106:} 青出于蓝 发表于 2026-9-23 10:31
看看
{:10_277:}
页:
[1]