1 条题解

  • 0
    @ 2026-6-19 10:30:12

    📝 题目大意

    给定一个长度为 1010 的字符串 SS,表示 1010 个保龄球瓶的状态(1 为竖立,0 为倒下)。球瓶按三角排列,共分为 77 列。判断当前排列是否为"分裂"(Split):球瓶 11 必须倒下,且存在两列都有竖立的球瓶,但它们之间有一列所有球瓶都倒下了。

    💡 解题思路

    1. 题目分析

      • 输入仅为长度 101001 串,没有复杂的数据范围约束。
      • 关键是将 1010 个球瓶映射到 77 列中,然后检测"两端有竖立、中间有空列"的模式。
      • 球瓶 11 竖立时直接排除(输出 No),这是必要条件。
    2. 算法推导

      • 列映射:根据题目图示,1010 个球瓶分别属于 77 列。标准代码中用数组 map[10] = {4,3,5,2,4,6,1,3,5,7} 表示球瓶 ii11-indexed)所属的列编号(171 \sim 7)。例如球瓶 1155 都属于第 44 列,球瓶 3399 都属于第 55 列。

      • 列状态压缩:遍历 SS,若某球瓶竖立(s[i] == '1'),则将对应列标记为 11col[map[i]-1] = 1)。这样 colcol 数组的每个元素表示该列是否有竖立的球瓶。

      • 分裂检测:从左到右扫描 77 列,维护三个布尔标志:

        • p1:是否已经遇到过有竖立球瓶的列。
        • p0:在遇到竖立列之后,是否遇到过空列(所有球瓶倒下的列)。
        • resp:在遇到空列之后,是否又遇到了竖立列。

        状态转移逻辑:

        • 当前列为竖立列(col[i] == 1)且 p1 为假 → 首次遇到竖立列,设置 p1 = true
        • 当前列为空列(col[i] == 0)且 p1 为真 → 在竖立列之后遇到空列,设置 p0 = true
        • 当前列为竖立列(col[i] == 1)且 p0 为真 → 在空列之后再次遇到竖立列,满足分裂条件,设置 resp = true

        这恰好对应了"两端竖立、中间有空列"的模式:p1 找到左端竖立列,p0 找到中间空列,resp 确认右端竖立列存在。

    3. 边界与细节

      • 球瓶 11s[0])必须为 '0',否则直接输出 No。这是题目明确要求的第一个条件。
      • 即使存在多组"分裂"模式,只要检测到一组即可输出 Yes
      • 列是从左到右扫描的,p1→p0→resp 的三段式检测天然保证了"中间列"在"两端列"之间。

    ⏱️ 复杂度分析

    • 时间复杂度O(1)O(1),输入始终为 1010 个字符,遍历和扫描均为常数操作。
    • 空间复杂度O(1)O(1),仅使用固定大小的数组 col[7] 和几个布尔变量。

    💻 标准代码 (C++)

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        string s;
        cin >> s;
        // 条件1:球瓶1必须倒下,否则直接输出No
        if(s[0] == '1'){
            cout << "No";
            return 0;
        }
        // map[i]:球瓶i+1所在的列编号(1~7)
        // 列1: 瓶7 | 列2: 瓶4 | 列3: 瓶2,8 | 列4: 瓶1,5 | 列5: 瓶3,9 | 列6: 瓶6 | 列7: 瓶10
        int map[10] = {4, 3, 5, 2, 4, 6, 1, 3, 5, 7};
        int col[7] = {0};  // 标记每列是否有竖立的球瓶
        // 遍历所有球瓶,将竖立球瓶所在的列标记为1
        for(int i = 0; i < 10; i++)
            if(s[i] == '1')
                col[map[i] - 1] = 1;
        // 三段式扫描检测分裂模式
        bool p1 = false;   // 是否遇到第一个竖立列
        bool p0 = false;   // 在竖立列之后是否遇到空列
        bool resp = false; // 在空列之后是否又遇到竖立列(即是否构成分裂)
        for(int i = 0; i < 7; i++){
            if(col[i] == 1 && p1 == false)
                p1 = true;   // 发现左端竖立列
            if(col[i] == 0 && p1 == true)
                p0 = true;   // 发现中间空列
            if(col[i] == 1 && p0 == true)
                resp = true; // 发现右端竖立列,满足分裂条件
        }
        if(resp)
            cout << "Yes";
        else
            cout << "No";
        return 0;
    }
    
    • 1

    信息

    ID
    629
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    (无)
    递交数
    1
    已通过
    1
    上传者