题解:AcWing 317 陨石的秘密
【题目来源】
AcWing:317. 陨石的秘密 - AcWing题库
【题目描述】
公元 \(11380\) 年,一颗巨大的陨石坠落在南极。
于是,灾难降临了,地球上出现了一系列反常的现象。
当人们焦急万分的时候,一支中国科学家组成的南极考察队赶到了出事地点。
经过一番侦察,科学家们发现陨石上刻有若干行密文,每一行都包含 \(5\) 个整数:
1 1 1 1 6
0 0 6 3 57
8 0 11 3 2845
著名的科学家 \(SS\) 发现,这些密文实际上是一种复杂运算的结果。
为了便于大家理解这种运算,他定义了一种 \(SS\) 表达式:
- \(SS\) 表达式是仅由
{,},[,],(,)组成的字符串。 - 一个空串是 \(SS\) 表达式。
- 如果 \(A\) 是 \(SS\) 表达式,且 \(A\) 中不含字符
{,},[,],则 \((A)\) 是 \(SS\) 表达式。 - 如果 \(A\) 是 \(SS\) 表达式,且 \(A\) 中不含字符
{,},则 \([A]\) 是 \(SS\) 表达式。 - 如果 \(A\) 是 \(SS\) 表达式,则 \({A}\) 是 SS 表达式。
- 如果 \(A\) 和 \(B\) 都是 \(SS\) 表达式,则 AB 也是 \(SS\) 表达式。
例如
()(())[]
{()[()]}
{{[[(())]]}}
都是 \(SS\) 表达式。
而
()([])()
[()
不是 \(SS\) 表达式。
一个 \(SS\) 表达式 \(E\) 的深度 \(D(E)\) 定义如下:

例如 (){()}[] 的深度为 \(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
【核心思想】
-
问题分析:给定三种括号的数量 \(L_1\)(
{})、\(L_2\)([])、\(L_3\)(())和深度限制 \(D\),求所有深度恰好为 \(D\) 的合法 \(SS\) 表达式的个数,结果对 \(11380\) 取模。这是一个动态规划问题,关键在于理解 \(SS\) 表达式的递归定义和深度计算方式。 -
算法选择:
- 动态规划(DP):定义
f[d][i][j][k]为深度不超过 \(d\),使用 \(i\) 对{}、\(j\) 对[]、\(k\) 对()的合法表达式个数 - 递推思想:根据最外层括号类型分类讨论,利用乘法原理和加法原理进行状态转移
- 容斥原理:深度恰好为 \(D\) 的方案数 = 深度不超过 \(D\) 的方案数 - 深度不超过 \(D-1\) 的方案数
- 动态规划(DP):定义
-
关键步骤:
- 初始化边界:
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]
- 枚举左边使用 \(p\) 对
- 情况2:最外层为
[]:左边部分不能含{},深度不超过 \(d-1\)- 枚举左边使用 \(q\) 对
[]$、$r$ 对()` f[d][i][j][k] += f[d-1][0][q][r] * f[d][i][j-q-1][k-r]
- 枚举左边使用 \(q\) 对
- 情况3:最外层为
():左边部分不能含{}和[],深度不超过 \(d-1\)- 枚举左边使用 \(r\) 对
() f[d][i][j][k] += f[d-1][0][0][r] * f[d][i][j][k-r-1]
- 枚举左边使用 \(r\) 对
- 情况1:最外层为
- 计算答案:
ans = (f[D][L1][L2][L3] - f[D-1][L1][L2][L3] + mod) % mod
- 初始化边界:
-
时间/空间复杂度:
- 时间复杂度:\(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数组
-
动态规划的核心思想:
- 状态定义:
f[d][i][j][k]表示深度不超过 \(d\) 时的方案数,便于递推计算 - 分类讨论:根据最外层括号类型(
{}、[]、())分别处理,利用 \(SS\) 表达式的递归定义 - 乘法原理:左边方案数 \(\times\) 右边方案数 = 当前分割方式的总方案数
- 加法原理:累加所有分割方式的方案数
- 容斥求恰好:通过「不超过 \(D\)」减「不超过 \(D-1\)」得到「恰好 \(D\)」
- 适用于计数类DP、括号序列、递归结构类问题
- 状态定义:
【解题思路】



【算法标签】
线性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
浙公网安备 33010602011771号