题解:AcWing 317 陨石的秘密

【题目来源】

AcWing:317. 陨石的秘密 - AcWing题库

【题目描述】

公元 \(11380\) 年,一颗巨大的陨石坠落在南极。

于是,灾难降临了,地球上出现了一系列反常的现象。

当人们焦急万分的时候,一支中国科学家组成的南极考察队赶到了出事地点。

经过一番侦察,科学家们发现陨石上刻有若干行密文,每一行都包含 \(5\) 个整数:

1 1 1 1 6
0 0 6 3 57
8 0 11 3 2845

著名的科学家 \(SS\) 发现,这些密文实际上是一种复杂运算的结果。

为了便于大家理解这种运算,他定义了一种 \(SS\) 表达式:

  1. \(SS\) 表达式是仅由 {}[]() 组成的字符串。
  2. 一个空串是 \(SS\) 表达式。
  3. 如果 \(A\) 是 \(SS\) 表达式,且 \(A\) 中不含字符 {}[],则 \((A)\) 是 \(SS\) 表达式。
  4. 如果 \(A\) 是 \(SS\) 表达式,且 \(A\) 中不含字符 {},则 \([A]\) 是 \(SS\) 表达式。
  5. 如果 \(A\) 是 \(SS\) 表达式,则 \({A}\) 是 SS 表达式。
  6. 如果 \(A\) 和 \(B\) 都是 \(SS\) 表达式,则 AB 也是 \(SS\) 表达式。

例如

()(())[]
{()[()]}
{{[[(())]]}}

都是 \(SS\) 表达式。

()([])()
[()

不是 \(SS\) 表达式。

一个 \(SS\) 表达式 \(E\) 的深度 \(D(E)\) 定义如下:

img/Pasted image 20260612085808.png

例如 (){()}[] 的深度为 \(2\)

密文中的复杂运算是这样进行的:

设密文中每行前 \(4\) 个数依次为 \(L_1\)\(L_2\)\(L_3\)\(D\),求出所有深度为 \(D\),含有 \(L_1\) 对 {}\(L_2\) 对 []\(L_3\) 对 () 的 \(SS\) 串的个数,并用这个数对当前的年份 \(11380\) 求余数,这个余数就是密文中每行的第 \(5\) 个数,我们称之为神秘数。

密文中某些行的第五个数已经模糊不清,而这些数字正是揭开陨石秘密的钥匙。

现在科学家们聘请你来计算这个神秘数。

【输入】

共一行,\(4\) 个整数 \(L_1\)\(L_2\)\(L_3\)\(D\)

【输出】

共一行,包含一个整数,即神秘数。

【输入样例】

1 1 1 2

【输出样例】

8

【核心思想】

  1. 问题分析:给定三种括号的数量 \(L_1\){})、\(L_2\)[])、\(L_3\)())和深度限制 \(D\),求所有深度恰好为 \(D\) 的合法 \(SS\) 表达式的个数,结果对 \(11380\) 取模。这是一个动态规划问题,关键在于理解 \(SS\) 表达式的递归定义和深度计算方式。

  2. 算法选择

    • 动态规划(DP):定义 f[d][i][j][k] 为深度不超过 \(d\),使用 \(i\){}\(j\)[]\(k\)() 的合法表达式个数
    • 递推思想:根据最外层括号类型分类讨论,利用乘法原理和加法原理进行状态转移
    • 容斥原理:深度恰好为 \(D\) 的方案数 = 深度不超过 \(D\) 的方案数 - 深度不超过 \(D-1\) 的方案数
  3. 关键步骤

    • 初始化边界f[d][0][0][0] = 1(空串,任意深度下都只有一种方案)
    • 状态转移(按深度 \(d\) 从小到大递推):
      • 情况1:最外层为 {}:左边部分深度不超过 \(d-1\),右边部分深度不超过 \(d\)
        • 枚举左边使用 \(p\){}$、$q$ 对 []\(、\)r$ 对 ()
        • f[d][i][j][k] += f[d-1][p][q][r] * f[d][i-p-1][j-q][k-r]
      • 情况2:最外层为 []:左边部分不能含 {},深度不超过 \(d-1\)
        • 枚举左边使用 \(q\)[]$、$r$ 对 ()`
        • f[d][i][j][k] += f[d-1][0][q][r] * f[d][i][j-q-1][k-r]
      • 情况3:最外层为 ():左边部分不能含 {}[],深度不超过 \(d-1\)
        • 枚举左边使用 \(r\)()
        • f[d][i][j][k] += f[d-1][0][0][r] * f[d][i][j][k-r-1]
    • 计算答案ans = (f[D][L1][L2][L3] - f[D-1][L1][L2][L3] + mod) % mod
  4. 时间/空间复杂度

    • 时间复杂度:\(O(D \cdot L_1 \cdot L_2 \cdot L_3 \cdot (L_1 \cdot L_2 \cdot L_3))\),四重循环嵌套枚举
    • 空间复杂度:\(O(D \cdot L_1 \cdot L_2 \cdot L_3)\),四维DP数组
  5. 动态规划的核心思想

    • 状态定义f[d][i][j][k] 表示深度不超过 \(d\) 时的方案数,便于递推计算
    • 分类讨论:根据最外层括号类型({}[]())分别处理,利用 \(SS\) 表达式的递归定义
    • 乘法原理:左边方案数 \(\times\) 右边方案数 = 当前分割方式的总方案数
    • 加法原理:累加所有分割方式的方案数
    • 容斥求恰好:通过「不超过 \(D\)」减「不超过 \(D-1\)」得到「恰好 \(D\)
    • 适用于计数类DP、括号序列、递归结构类问题

【解题思路】

img/Pasted image 20260612090241.png
img/Pasted image 20260612090925.png
img/Pasted image 20260612215312.png

【算法标签】

线性DP-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// 定义模数常量
const int mod = 11380;

// 全局变量声明
int L1, L2, L3, D;              // L1, L2, L3: 三种括号的数量, D: 最大嵌套深度
int f[35][15][15][15];          // 动态规划数组,f[d][i][j][k]表示深度不超过d时的方案数

// 主函数入口
int main()
{
    // 读取输入:三种括号的数量和最大深度
    cin >> L1 >> L2 >> L3 >> D;

    // 初始化边界条件:没有括号时,任何深度下都只有一种方案(空串)
    for (int i = 0; i <= D; i++)
        f[i][0][0][0] = 1;

    // 动态规划:按深度从小到大递推
    for (int d = 1; d <= D; d++)
        for (int i = 0; i <= L1; i++)           // 第一种括号的数量
            for (int j = 0; j <= L2; j++)       // 第二种括号的数量
                for (int k = 0; k <= L3; k++)   // 第三种括号的数量
                {
                    // 情况1:最外层为第一种括号
                    if (i > 0)
                    {
                        // 枚举左边部分使用的括号数量
                        for (int p = 0; p < i; p++)
                            for (int q = 0; q <= j; q++)
                                for (int r = 0; r <= k; r++)
                                    f[d][i][j][k] = (f[d][i][j][k] +
                                        f[d - 1][p][q][r] * f[d][i - p - 1][j - q][k - r]) % mod;
                    }

                    // 情况2:最外层为第二种括号
                    if (j > 0)
                    {
                        // 左边不能有第一种括号,枚举第二、三种括号的使用数量
                        for (int q = 0; q < j; q++)
                            for (int r = 0; r <= k; r++)
                                f[d][i][j][k] = (f[d][i][j][k] +
                                    f[d - 1][0][q][r] * f[d][i][j - q - 1][k - r]) % mod;
                    }

                    // 情况3:最外层为第三种括号
                    if (k > 0)
                    {
                        // 左边不能有第一、二种括号,枚举第三种括号的使用数量
                        for (int r = 0; r < k; r++)
                            f[d][i][j][k] = (f[d][i][j][k] +
                                f[d - 1][0][0][r] * f[d][i][j][k - r - 1]) % mod;
                    }
                }

    // 输出结果:深度恰好为D的方案数 = 深度不超过D的方案数 - 深度不超过D-1的方案数
    if (D == 0)
        cout << f[D][L1][L2][L3] % mod << endl;
    else
        cout << (f[D][L1][L2][L3] - f[D - 1][L1][L2][L3] + mod) % mod << endl;

    return 0;
}

【运行结果】

1 1 1 2
8
posted @ 2026-06-14 10:36  团爸讲算法  阅读(11)  评论(0)    收藏  举报