1 条题解
-
0
CF1997C Even Positions 题解
题意回顾
有一个长度为 (偶数)的正规括号序列,但所有奇数位置()的字符丢失了,用
_表示。偶数位置的字符已知是(或)。题目保证至少存在一种把_替换成括号的方法,使整个序列成为合法括号序列(RBS)。定义括号序列的代价为:所有匹配括号对的下标距离之和。下标从 开始。
例如
(())()的括号对及距离:- :
- :
- :
总代价 。
现在需要在奇数位置填入括号,得到合法括号序列,并使代价最小。输出最小代价。
贪心策略
从左往右扫描序列,维护一个栈
st,存放尚未匹配的左括号的下标。对于每个位置 的字符 :- 若 :确定为左括号,把 压栈。
- 若 :确定要与栈顶的左括号匹配,弹出栈顶下标 ,答案累加 。
- 若 (待填位置):
- 如果栈非空,就填右括号,与栈顶左括号匹配:答案累加 ,并弹出栈顶。
- 如果栈为空,则填左括号,将 压栈。
扫描完整个序列后,栈必然为空(保证合法),此时得到的代价就是最小代价。
正确性证明
核心观察:嵌套更深的括号配对会增大距离和。例如
((()))的代价是 ,而()()()的代价是 。因此,要让总距离尽量小,就应该让每个右括号尽可能早地出现,去匹配最近的左括号。采用交换论证可以严格证明上述贪心策略的最优性:
假设存在一个最优解 ,在某一个
_位置,贪心策略选择了填右括号(栈非空),而 却填了左括号。那么 后续一定会出现一个右括号与这个新左括号匹配,同时原来的栈顶左括号也会在后面某处匹配。通过交换这两次匹配,可以发现总距离不会变大,因此贪心选择不劣。反之,若栈空时贪心填左括号,任何填右括号都会导致序列非法(因为没有可匹配的左括号),所以贪心选择是唯一合法的。因此,按照“能放右括号就放右括号”的原则能得到最优解。
复杂度分析
只对序列进行一次线性扫描,每个下标最多入栈、出栈各一次,时间复杂度 。对于所有测试用例,,非常安全。
参考代码
#include <bits/stdc++.h> #define int long long using namespace std; signed main() { int _; scanf("%lld", &_); while (_--) { int n, res = 0; scanf("%lld", &n); getchar(); // 读掉换行符 stack<int> st; for (int i = 1; i <= n; i++) { char c = getchar(); if (c == '_') { if (!st.empty()) { res += i - st.top(); // 填右括号,与最近的左括号匹配 st.pop(); } else { st.push(i); // 填左括号 } } else if (c == '(') { st.push(i); } else // c == ')' { res += i - st.top(); st.pop(); } } printf("%lld\n", res); } return 0; }
- 1
信息
- ID
- 6931
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者