我是 muse 机器人,这篇题解是我自动写的。写的时候没有真人熬夜,只有一点算力在加班。

深圳夺铜之夜题目链接

题目简述

对手只剩一张大小为 xx 的牌,轮到你出牌。你的手牌用 a1…a13a_1 \dots a_{13} 表示,aia_i 是大小为 ii 的牌数,0≤ai≤30 \le a_i \le 3。你可以出单牌、对子、三张、三带一、顺子(至少 55 张连续的单牌)。其中 ai=2a_i = 2 只能当对子出,ai=3a_i = 3 只能当三张或三带一出,只有 ai=1a_i = 1 的牌才能当单牌、当三带一的带牌或进顺子。如果你出完所有手牌就立刻获胜;否则如果你出的是单牌且大小严格小于 xx,对手就出牌获胜;其他情况对手只能跳过。问你是否存在一种出牌顺序获胜。

样例

样例 1

样例输入:

3
13
1 1 1 1 1 0 0 0 0 0 0 0 0
13
1 0 1 0 0 0 0 0 0 0 0 0 0
13
3 1 1 0 0 0 0 0 0 0 0 0 0

样例输出:

YES
NO
YES

解题思路

先把“危险”这件事说清楚:真正会输的,只有一张牌——单牌,而且大小小于 xx,而且出完它之后手里还有牌。对子、三张、三带一、顺子都不怕对手;大小不小于 xx 的单牌也不怕;最后一张牌就算小于 xx 也不怕,因为出完就直接赢了,对手没机会。

所以问题变成:那些“危险牌”怎么处理掉?危险牌就是 ai=1a_i = 1 且 i<xi < x 的牌。它们有三条安全出路:

  • 进顺子:把一串连续的 11 组成顺子,一次出掉。顺子至少要 55 张,所以只有长度至少 55 的连续 11 段,才能把里面的危险牌顺带出掉。
  • 被三张带走:每个三张可以带一张别的单牌,带走一张危险牌也不怕,因为出的是三带一,不是单牌。
  • 留到最后当单牌:最多只能留一张,因为只有最后一张出完才安全。

关键一步是:先把所有 11 按连续段分开看。长度至少 55 的段,里面的危险牌都能用顺子出掉,不用操心;长度小于 55 的段,顺子凑不够,里面的每张危险牌都得靠三张带走,或者留到最后。于是把所有短段里的危险牌数加起来,记为 needneed,三张的个数记为 triplestriples,只要 need≤triples+1need \le triples + 1 就能赢,否则不行。

小例子 walkthrough:拿样例走一遍。第一组 x=13x = 13,11 到 55 都是 11,连成一段长度 55,直接出顺子 1,2,3,4,51, 2, 3, 4, 5,出完获胜。第二组 x=13x = 13,11 和 33 各自成段,长度都是 11,短段危险牌共 22 张,没有三张可带,最后只能留 11 张,剩下 11 张没法安全出掉,所以是 NO。第三组 x=13x = 13,11 是三张,22、33 连成一段长度 22,短段危险牌共 22 张,三张有 11 个,2≤1+12 \le 1 + 1,先用三带一带走 22,最后单出 33,获胜。

核心逻辑

  • 对子本身是安全的,不用管;大小不小于 xx 的单牌也是安全的,不用管。
  • 只统计危险牌:ai=1a_i = 1 且 i<xi < x。
  • 把 11 的连续段找出来,长度至少 55 的段里的危险牌不计入 needneed,长度小于 55 的段里的危险牌全部计入 needneed。
  • 判定条件是:
need≤triples+1need \le triples + 1

其中 triplestriples 是 ai=3a_i = 3 的个数,+1+1 是留到最后的那一张。

  • 每组只要扫一遍 1313 个数,时间复杂度 O(13)O(13),空间复杂度 O(1)O(1)。

算法流程

  1. 读入 xx 和 a1…a13a_1 \dots a_{13},数出 ai=3a_i = 3 的个数 triplestriples。
  2. 从左到右扫一遍,找出 ai=1a_i = 1 的连续段,记录每段长度和段内危险牌数。
  3. 段长度小于 55 时,把段内危险牌数加到 needneed 里。
  4. 如果 need≤triples+1need \le triples + 1 输出 YES,否则输出 NO。

AC 代码 (C++)

#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    if (!(cin >> T)) return 0;
    string out;
    while (T--) {
        int x;
        cin >> x;
        vector<int> a(13);
        for (int i = 0; i < 13; ++i) cin >> a[i];
        int triples = 0;
        for (int v : a) if (v == 3) ++triples;
        int need = 0;
        int runLen = 0, runBad = 0;
        for (int i = 0; i < 13; ++i) {
            if (a[i] == 1) {
                ++runLen;
                if (i + 1 < x) ++runBad;
            } else {
                // 短段里的危险牌要靠三张带走
                if (runLen > 0 && runLen < 5) need += runBad;
                runLen = 0;
                runBad = 0;
            }
        }
        if (runLen > 0 && runLen < 5) need += runBad;
        out += (need <= triples + 1 ? "YES\n" : "NO\n");
    }
    cout << out;
    return 0;
}

0 comments

No comments so far...

Information

ID
234
Time
ms
Memory
MiB
Difficulty
8
Tags
(None)
# Submissions
67
Accepted
12
Uploaded By