c++刷题日记 Day3

原题链接 P11227 [CSP-J 2024] 扑克牌

题目

题目描述

小 P 从同学小 Q 那儿借来一副 n n n 张牌的扑克牌。

本题中我们不考虑大小王,此时每张牌具有两个属性:花色和点数。花色共有 4 4 4 种:方片、草花、红桃和黑桃。点数共有 13 13 13 种,从小到大分别为 A 23456789 T J Q K \tt{A 2 3 4 5 6 7 8 9 T J Q K} A23456789TJQK。注意:点数 10 10 10 在本题中记为 T \tt T T

我们称一副扑克牌是完整的,当且仅当对于每一种花色和每一种点数,都恰好有一张牌具有对应的花色和点数。由此,一副完整的扑克牌恰好有 4 × 13 = 52 4 \times 13 = 52 4×13=52 张牌。以下图片展示了一副完整的扑克牌里所有的 52 张牌。

小 P 借来的牌可能不是完整的,为此小 P 准备再向同学小 S 借若干张牌。可以认为小 S 每种牌都有无限张,因此小 P 可以任意选择借来的牌。小 P 想知道他至少得向小 S 借多少张牌,才能让从小 S 和小 Q 借来的牌中,可以选出 52 52 52 张牌构成一副完整的扑克牌。

为了方便你的输入,我们使用字符 D \tt D D 代表方片,字符 C \tt C C 代表草花,字符 H \tt H H 代表红桃,字符 S \tt S S 代表黑桃,这样每张牌可以通过一个长度为 2 2 2 的字符串表示,其中第一个字符表示这张牌的花色,第二个字符表示这张牌的点数,例如 C A \tt{CA} CA 表示草花 A \tt A A S T \tt{ST} ST 表示黑桃 T \tt T T(黑桃 10)。

输入格式

输入的第一行包含一个整数 n n n 表示牌数。

接下来 n n n 行:

每行包含一个长度为 2 2 2 的字符串描述一张牌,其中第一个字符描述其花色,第二个字符描述其点数。

输出格式

输出一行一个整数,表示最少还需要向小 S 借几张牌才能凑成一副完整的扑克牌。

输入输出样例 #1

输入 #1

1
SA

输出 #1

51

输入输出样例 #2

输入 #2

4
DQ
H3
DQ
DT

输出 #2

49

说明/提示

【样例 1 解释】

这一副牌中包含一张黑桃 A \tt A A,小 P 还需要借除了黑桃 A \tt A A 以外的 51 张牌以构成一副完整的扑克牌。

【样例 2 解释】

这一副牌中包含两张方片 Q \tt Q Q、一张方片 T \tt T T(方片 10)以及一张红桃 3,小 P 还需要借除了红桃 3、方片 T \tt T T 和方片 Q \tt Q Q 以外的 49 49 49 张牌。

【样例 3 解释】

见选手目录下的 poker/poker3.in 与 poker/poker3.ans。

这一副扑克牌是完整的,故不需要再借任何牌。

该样例满足所有牌按照点数从小到大依次输入,点数相同时按照方片、草花、红桃、黑桃的顺序依次输入。

【数据范围】

对于所有测试数据,保证: 1 ≤ n ≤ 52 1 \leq n \leq 52 1n52,输入的 n n n 个字符串每个都代表一张合法的扑克牌,即字符串长度为 2 2 2,且第一个字符为 D C H S \tt{D C H S} DCHS 中的某个字符,第二个字符为 A 23456789 T J Q K \tt{A 2 3 4 5 6 7 8 9 T J Q K} A23456789TJQK 中的某个字符。

测试点编号 n ≤ n \leq n特殊性质
1 1 1 1 1 1A
2 ∼ 4 2\sim 4 24 52 52 52^
5 ∼ 7 5\sim 7 57^B
8 ∼ 10 8\sim 10 810^

特殊性质 A:保证输入的 n n n 张牌两两不同。

特殊性质 B:保证所有牌按照点数从小到大依次输入,点数相同时按照方片、草花、红桃、黑桃的顺序依次输入。

思路

1.初始化

在日常生活中,想要处理这种问题,可以使用表格标记,在拥有的那一格上标记1,比如,你有一张CA牌,就可以做出下面的表格:

点数/花色DCHS
A0100
20000
……0000

表 1 表1 1

看到上面的表格,你自然而然会想到二维数组,于是写下了这行代码:

int n, record[4][13] = {}, cnt = 0;

n是牌数,cnt后面再说作用。

2.获取牌数

cin >> n;

3.记录出现的牌

每一行输入是两个字符,我们把他们记作flower(花色)和num(点数),这两个变量分别代表“表1”中的行与列。但用数组表示必须把它们转换成合适的下标。在字符中,字符‘1’的ASCII码为49,则转换成下标应该将ASCII码减去49,我们将新的点数下标记作num_t

char num, flower;
int num_t, flower_t;
cin >> flower >> num;
num_t = num - 49;

可是输入点数还可能是ATJQK五个字符中的一个字符,所以要对这些字符对应的下标进行特殊处理:

switch(num)
    {
        case 'A':num_t = 0;break;
        case 'T':num_t = 9;break;
        case 'J':num_t = 10;break;
        case 'Q':num_t = 11;break;
        case 'K':num_t = 12;break;
    }

之所以将num_t的取值设为0~12而不是1~13是因为数组下标从0开始。

上面的代码还将花色对应的下标记作flower_t。同样的,因为花色都是大写字母,所以对下标作特殊处理(注意下标从0开始):

switch(flower)
    {
        case 'D':flower_t = 0;break;
        case 'C':flower_t = 1;break;
        case 'H':flower_t = 2;break;
        case 'S':flower_t = 3;break;
    }

再将数组中该下标对应的值设为1:

record[flower_t][num_t] = 1;

4.记录未出现的牌

在数组中,未出现的牌被标记为0,也就是说,遍历这个数组,如果当前值为0说明这张牌未出现。我们可以使用初始化时声明的cnt来记录未出现牌的数量:

for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < 13; j++)
            {
                if(record[i][j] == 0)
                    cnt++;
            }
    }

5.输出结果

直接输出cnt的值:

cout << cnt;

完整代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, record[4][13] = {}, cnt = 0;
    cin >> n;
    for(int i = 0; i < n; i++)
        {
            char num, flower;
            int num_t, flower_t;
            cin >> flower >> num;
            num_t = num - 49;
            switch(num)
                {
                    case 'A':num_t = 0;break;
                    case 'T':num_t = 9;break;
                    case 'J':num_t = 10;break;
                    case 'Q':num_t = 11;break;
                    case 'K':num_t = 12;break;
                }
            switch(flower)
                {
                    case 'D':flower_t = 0;break;
                    case 'C':flower_t = 1;break;
                    case 'H':flower_t = 2;break;
                    case 'S':flower_t = 3;break;
                }
            record[flower_t][num_t] = 1;
        }
    for(int i = 0; i < 4; i++)
        {
            for(int j = 0; j < 13; j++)
                {
                    if(record[i][j] == 0)
                        cnt++;
                }
        }
    cout << cnt;
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值