# T640659 【MGVOI R1-C】收集括号(brackets) ## 题目描述 本题中...
创建于:2025年8月16日
使用 GPT-5 Thinking 回答 Chat01
创建于:2025年8月16日
使用 GPT-5 Thinking 回答 Chat01
本题中 合法括号串 的定义如下:
::::info[合法括号串的定义]{open}
()
是合法括号串。A
是合法括号串,则 (A)
也是合法括号串。A
,B
均为合法括号串,则 AB
也是合法括号串。::::
Alice 和 Bob 正在合作玩一款叫做“收集括号”的游戏!这个游戏总共分为以下三步流程:
::::success[第一步:初始化]{open}
首先,计算机会自动生成一个 行 列的方格图,其中第 行第 列的方格对应的坐标为 。例如,左上角方格的坐标为 ,右下角方格的坐标为 。
然后,计算机会在每个方格中都填入一个字符(从 L
,R
,X
中选择)。若某个方格中的字符为 L
,则表示方格中有一个左括号;若为 R
,则表示方格中有一个右括号;若为 X
,则表示方格中有一个障碍物。
::::
::::success[第二步:Alice 的行动回合]{open}
在第一步流程完全结束之后,Alice 可以对方格图进行任意次(包括 次)反转操作。
在一次反转操作中,Alice 首先需要选择方格图的 某一行或某一列 作为这次操作的范围。
之后,计算机将遍历 Alice 选择的这一行(或这一列)。对于每一个范围内的方格(除了障碍物),计算机都会反转这个方格上的字符。也就是说,如果方格上原先的字符是 L
,那么就将其改为 R
;如果原先是 R
,那么就将其改为 L
;如果原先是 X
,那么不做任何改动。
对于这一次反转操作而言,如果 Alice 选择了第 行()作为反转范围,那么需要花费 枚金币;如果她选择了第 列()作为反转范围,那么需要花费 枚金币。
::::
::::success[第三步:Bob 的行动回合]{open}
在第二步流程完全结束之后,Bob 将从坐标为 的方格处(也就是方格图的左上角)出发,开始收集方格图中的括号。
在任意时刻,Bob 都可以选择 向正下方或正右方 移动一个方格(前提是要到达的位置既不超过方格图的边界,也没有障碍物)。也就是说,如果 Bob 位于方格 ,那么他下一步就可以前往方格 或者方格 ,只要他保证自己 始终位于方格图的范围内,并且不会前往有障碍物的方格。
Bob 每到达一个方格,就会收集这个方格中的括号。在抵达坐标为 的终点方格(也就是方格图的右下角)之后,他会整理自己收集到的所有括号(包括起点和终点方格的括号),并将其 由先到后按照收集的顺序 排成一个字符串 。
如果 是一个合法括号串,则 Alice 和 Bob 在这局游戏中共同获胜;否则他们在这局游戏中落败。(如果 Bob 无法到达终点方格,则也认为他们落败)
::::
注意: 我们假设 Bob 是绝顶聪明的,也就是说,在 Alice 的所有操作完成之后,只要存在任何一种符合上述规则的行动方式能让他们获胜,Bob 就会采用这种行动方式。
在计算机已经填满方格图的情况下(即第一步的初始化流程已经完成),请你帮 Alice 判断,是否存在一种操作方案,使得她能够和 Bob 共同获胜?如果存在,则她最少需要花费多少枚金币来取胜?
每个测试点包含多组测试数据,各组测试数据之间相互独立。
第一行包含一个正整数 ,表示测试数据的组数。
对于每组测试数据:
第一行包含两个正整数 ,分别表示方格图的行数和列数。保证 是一个奇数,这意味着 Bob 最终得到的字符串 的长度一定为偶数。
第二行包含 个正整数,其中第 个正整数 表示在方格图的第 行进行反转操作需花费的金币数量。
第三行包含 个正整数,其中第 个正整数 表示在方格图的第 列进行反转操作需花费的金币数量。
接下来 行,每行包含一个长度为 的字符串(仅含 L
,R
,X
三种字符),其中第 行第 个字符即为计算机在方格 中填入的字符。保证左上角和右下角方格中的字符不为 X
。
对于每组测试数据,仅需在单独一行输出一个整数:
如果 Alice 有可能和 Bob 共同获胜,则输出她最少需要花费的金币数;
否则,输出 -1
。
text3 1 4 1 1 1 1 1 LXXR 1 4 1 1 1 1 1 LLRR 2 3 1 1 1 1 1 LRR XRL
text-1 0 1
text4 4 3 1 1 1 9 1 1 1 LLL LXL LXL LLL 4 3 1 1 1 1 1 1 1 LLL LXL LXL LLL 4 5 8 5 6 3 8 5 6 5 3 RRRRR RRXXR XRRRL RXLLR 7 10 10 100 1 1 100 1 10 10 1 1 1 1 1 1 1 1 10 RLLLLLLLXX RXLXLXLLRL RLLLLLXLLL LLXXRRRXLX LLLLLRLLLX XLLLXLXLLR LLXLXLLXLL
text2 1 13 22
【样例 #1】
::::info[样例 #1 解释]
对于第一组测试数据,计算机生成的方格图为 LXXR
。由于中间两个障碍物的阻挡,Bob 无法从方格 向右移动到方格 ,故 Alice 和 Bob 不可能获胜,输出 -1
;
对于第二组测试数据,计算机生成的方格图为 LLRR
。显然,Bob 可以直接从方格 向右移动到方格 ,最终得到的 就是一个合法括号串。因此,Alice 无需花费任何金币进行反转操作即可获胜,输出 0
;
对于第三组测试数据,Alice 只需花费 枚金币对第三列使用一次反转操作。在这之后,方格图的状态变为:
Bob 只需按照橙色方格对应的路径行动,最终可以得到 ,这是一个合法括号串。
容易证明,要让他们获胜最少需要 枚金币,故输出 1
。
::::
【样例 #2】
::::info[样例 #2 解释]
:::success[第一组测试数据]
对于第一组测试数据,Alice 可以分别对第二行和第三列使用反转操作。在这之后,方格图的状态变为:
Bob 只需按照橙色方格对应的路径行动,最终可以得到 ,这是一个合法括号串。
Alice 总共需要花费 枚金币,可以证明为最小花费。
:::
:::success[第二组测试数据]
对于第二组测试数据,Alice 可以对第四行使用反转操作。在这之后,方格图的状态变为:
Bob 只需按照橙色方格对应的路径行动,最终可以得到 ,这是一个合法括号串。
Alice 总共需要花费 枚金币,可以证明为最小花费。
:::
:::success[第三组测试数据]
对于第三组测试数据,Alice 可以分别对第一行、第二行使用反转操作。在这之后,方格图的状态变为:
Bob 只需按照橙色方格对应的路径行动,最终可以得到 ,这是一个合法括号串。
Alice 总共需要花费 枚金币,可以证明为最小花费。
:::
:::success[第四组测试数据]
对于第四组测试数据,Alice 可以分别对第一行、第六行、第七行、第二列使用反转操作。在这之后,方格图的状态变为:
Bob 只需按照橙色方格对应的路径行动,最终可以得到 ,这是一个合法括号串。(注:括号串的颜色仅为方便观察,与答案无关)
Alice 总共需要花费 枚金币,可以证明为最小花费。
:::
::::
【样例 #3】
见附件中的 brackets/brackets3.in
与 brackets/brackets3.ans
。
这个样例满足测试点 的限制。
【样例 #4】
见附件中的 brackets/brackets4.in
与 brackets/brackets4.ans
。
这个样例满足测试点 的限制。
【样例 #5】
见附件中的 brackets/brackets5.in
与 brackets/brackets5.ans
。
这个样例满足测试点 的限制。
【数据范围】
对于所有测试点,保证 ,( 为奇数),,并且方格图中初始填入的字符仅含 L
,R
,X
,其中左上角和右下角的字符一定不为 X
。
::cute-table{tuack}
测试点编号 | 特殊性质 | |||
---|---|---|---|---|
无 | ||||
^ | ||||
A | ||||
^ | ^ | 无 |
特殊性质 A:保证 。
下面给出一种可过全部数据的做法与 C++ 代码(已通过题面样例)。
L
为 '('
,R
为 ')'
。把每个非障碍格的初始括号转成比特 v[i][j]
:L=0, R=1
。r[i]
、每列一个比特 c[j]
,格子最终括号比特为 v[i][j] ^ r[i] ^ c[j]
。(1,1)
到 (n,m)
,单调向右或向下,长度固定为 K=n+m-1
(偶数)。路径上收集的串需为合法括号串。r[i] ^ c[j] = d
(其中 d = v[i][j] ^ s
,s
为该格希望放的括号比特)把路径上的格子连接起来自然形成一棵树,因此总能用两种“根比特”解(整体取反等价)满足任意给定的括号序列。t∈{0,1}
只会把所有变量整体取反,代价在 cost0
与 S-cost0
(S=∑a_i+∑b_j
)二者之间取较小即可。(1,1)
到 (n,m)
的单调路径都会经过所有行与所有列,所以 S
与路径无关。于是问题化为:
t=0
的前提下,在所有合法路径与所有合法括号序列中,求能达到的 cost0
的最小值与最大值;min(minCost0, S - maxCost0)
;若不可行则 -1
。cost0
?
(i,j)
且已经使用该格子的括号后,记录
k
(前缀合法:k≥0
,并且最后需回到 0
);ri=r[i]
与当前列比特 cj=c[j]
(二者在到达该格后都已确定)。ri
已知;我们可在 (i,j)
选择本格 s∈{0,1}
(受 k
限制),据 cj = ri ^ v[i][j] ^ s
首次确定新列,代价加 b[j]*cj
;cj
已知;选择 s
后,ri = cj ^ v[i][j] ^ s
首次确定新行,代价加 a[i]*ri
。(1,1)
:规定根取 t=0
,因此 r[1]=0
;根据合法串要求,首字符必须为 '('
(s=0
),从而 c[1]=v[1][1]
,初始栈深 k=1
,代价加 b[1]*c[1]
。(n,m)
后要求 k=0
。minCost0
,再用同样转移把“取最小”改成“取最大”得到 maxCost0
。i≤100, j≤100, k≤(n+m-1)/2≤99, ri,cj∈{0,1}
,总复杂度 O(n*m*K)
,常数小,内存约数十 MB。cpp#include <bits/stdc++.h> using namespace std; using i64 = long long; const i64 INF = (i64)4e18; static i64 dpMin[105][105][105][4]; static i64 dpMax_[105][105][105][4]; static int vch[105][105]; // -1 for X, 0 for L, 1 for R static i64 arow[105], bcol[105]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; if(!(cin >> T)) return 0; while (T--) { int n, m; cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> arow[i]; for (int j = 1; j <= m; ++j) cin >> bcol[j]; string s; for (int i = 1; i <= n; ++i) { cin >> s; for (int j = 1; j <= m; ++j) { char c = s[j-1]; if (c == 'X') vch[i][j] = -1; else if (c == 'L') vch[i][j] = 0; else vch[i][j] = 1; // 'R' } } // 若起点或终点为障碍(题面保证不会发生) if (vch[1][1] == -1 || vch[n][m] == -1) { cout << -1 << "\n"; continue; } int K = n + m - 1; // 总格数(偶数) int Kmax = K / 2; // 栈深最大值 i64 S = 0; for (int i = 1; i <= n; ++i) S += arow[i]; for (int j = 1; j <= m; ++j) S += bcol[j]; // 初始化 DP 数组 // dpMin -> INF, dpMax_ -> -INF for (int i = 0; i <= n; ++i) for (int j = 0; j <= m; ++j) for (int k = 0; k <= Kmax; ++k) for (int t = 0; t < 4; ++t) { dpMin[i][j][k][t] = INF; dpMax_[i][j][k][t] = -INF; } // 起点 (1,1):r1=0(根取 t=0),首字符必须为 '('(s=0) if (vch[1][1] != -1) { int r1 = 0; int s0 = 0; // '(' int c1 = r1 ^ vch[1][1] ^ s0; // c1 = v ^ s (因 r1=0) i64 cost0 = bcol[1] * (i64)c1; int k = 1; // 已放一个 '(' int state = (r1 << 1) | c1; // 高位是 ri, 低位是 cj dpMin[1][1][k][state] = cost0; dpMax_[1][1][k][state] = cost0; } auto allowedK = [&](int d)->int { // d = 已访问格数 = i+j-1 int up = min(d, K - d); if (up < 0) up = -1; return up; }; // 转移 for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { if (i == 1 && j == 1) continue; if (vch[i][j] == -1) continue; // 障碍,不能进入 int d = i + j - 1; int upNew = allowedK(d); if (upNew < 0) continue; // 从左转移:来源 (i, j-1) if (j > 1 && vch[i][j-1] != -1) { int dPrev = d - 1; int upPrev = allowedK(dPrev); for (int kPrev = 0; kPrev <= upPrev; ++kPrev) { for (int st = 0; st < 4; ++st) { i64 curMin = dpMin[i][j-1][kPrev][st]; i64 curMax = dpMax_[i][j-1][kPrev][st]; if (curMin == INF && curMax == -INF) continue; int ri = (st >> 1) & 1; // 本行已知 // s=0 '(' { int kNew = kPrev + 1; if (kNew <= upNew) { int cj = ri ^ vch[i][j] ^ 0; int stNew = (ri << 1) | cj; i64 add = bcol[j] * (i64)cj; if (curMin != INF) dpMin[i][j][kNew][stNew] = min(dpMin[i][j][kNew][stNew], curMin + add); if (curMax != -INF) dpMax_[i][j][kNew][stNew] = max(dpMax_[i][j][kNew][stNew], curMax + add); } } // s=1 ')',需要 kPrev>0 if (kPrev > 0) { int kNew = kPrev - 1; if (kNew <= upNew) { int cj = ri ^ vch[i][j] ^ 1; int stNew = (ri << 1) | cj; i64 add = bcol[j] * (i64)cj; if (curMin != INF) dpMin[i][j][kNew][stNew] = min(dpMin[i][j][kNew][stNew], curMin + add); if (curMax != -INF) dpMax_[i][j][kNew][stNew] = max(dpMax_[i][j][kNew][stNew], curMax + add); } } } } } // 从上转移:来源 (i-1, j) if (i > 1 && vch[i-1][j] != -1) { int dPrev = d - 1; int upPrev = allowedK(dPrev); for (int kPrev = 0; kPrev <= upPrev; ++kPrev) { for (int st = 0; st < 4; ++st) { i64 curMin = dpMin[i-1][j][kPrev][st]; i64 curMax = dpMax_[i-1][j][kPrev][st]; if (curMin == INF && curMax == -INF) continue; int cj = st & 1; // 本列已知 // s=0 '(' { int kNew = kPrev + 1; if (kNew <= upNew) { int ri = cj ^ vch[i][j] ^ 0; int stNew = (ri << 1) | cj; i64 add = arow[i] * (i64)ri; if (curMin != INF) dpMin[i][j][kNew][stNew] = min(dpMin[i][j][kNew][stNew], curMin + add); if (curMax != -INF) dpMax_[i][j][kNew][stNew] = max(dpMax_[i][j][kNew][stNew], curMax + add); } } // s=1 ')',需要 kPrev>0 if (kPrev > 0) { int kNew = kPrev - 1; if (kNew <= upNew) { int ri = cj ^ vch[i][j] ^ 1; int stNew = (ri << 1) | cj; i64 add = arow[i] * (i64)ri; if (curMin != INF) dpMin[i][j][kNew][stNew] = min(dpMin[i][j][kNew][stNew], curMin + add); if (curMax != -INF) dpMax_[i][j][kNew][stNew] = max(dpMax_[i][j][kNew][stNew], curMax + add); } } } } } } } i64 bestMin = INF, bestMax = -INF; for (int st = 0; st < 4; ++st) { bestMin = min(bestMin, dpMin[n][m][0][st]); bestMax = max(bestMax, dpMax_[n][m][0][st]); } if (bestMin == INF) { cout << -1 << "\n"; } else { // 根取 t=0 的最小 cost0 为 bestMin;根取 t=1 等价于 S - cost0,取其中可能的最小即 S - 最大 cost0 i64 ans = min(bestMin, S - bestMax); cout << ans << "\n"; } } return 0; }
O(n*m*K)
,其中 K=(n+m-1)/2≤99
,转移常数小;时间与内存均满足题目范围。