题解:洛谷 P3056 Clumsy Cows
【题目来源】
洛谷:P3056 [USACO12NOV] Clumsy Cows S - 洛谷
【题目描述】
奶牛 Bessie 正试图在她的新笔记本电脑上输入一个平衡的括号字符串,但由于她的蹄子很大(因此非常笨拙),她不断地输入错误的字符。请通过计算要使该字符串变为平衡,最少需要反转多少个字符(例如,将左括号变为右括号,或反之)来帮助她。
对于“括号字符串平衡”有几种定义方法。或许最简单的定义是,字符串中的 ( 和 ) 总数必须相同,并且对于字符串的任何前缀,( 的数量必须不少于 ) 的数量。例如,以下字符串都是平衡的:
()
(())
()(()())
而以下则不平衡:
)(
())(
((())))
【输入】
一个只包含(和)的字符串,长度为偶数,最多 \(100000\) 个字符。
【输出】
一个整数,表示要将该字符串变为平衡字符串,最少需要反转的括号数量。
【输入样例】
())(
【输出样例】
2
【核心思想】
-
问题分析:给定一个由
(和)组成的字符串,求最少反转多少个括号使其变为平衡字符串。平衡的定义:左右括号数量相等,且任意前缀中(的数量不少于)的数量。这是一个栈模拟/贪心问题,关键在于用栈处理匹配,统计不匹配的括号数量。 -
算法选择:
- 栈模拟匹配:遍历字符串,遇到
(入栈,遇到)尝试出栈。若栈空时遇到),说明需要反转(num1++),并将反转后的(入栈 - 剩余左括号处理:遍历结束后栈中剩余的
(需要一半改为)(num2/2)
- 栈模拟匹配:遍历字符串,遇到
-
关键步骤:
- 读取输入:字符串 \(S\)
- 栈处理(遍历每个字符):
- 若为
(:入栈 - 若为
):- 若栈空:\(num1 \mathrel{+}= 1\)(需要反转),将
(入栈(模拟反转) - 否则:出栈(匹配成功)
- 若栈空:\(num1 \mathrel{+}= 1\)(需要反转),将
- 若为
- 统计剩余:栈中剩余左括号数 = \(num2\),需要 \(num2/2\) 次反转
- 输出 \(num1 + num2/2\)
-
时间/空间复杂度:
- 时间复杂度:\(O(|S|)\)
- 空间复杂度:\(O(|S|)\),栈空间
-
栈匹配的核心思想:
- 贪心匹配:遇到
)时优先与栈顶(匹配,无法匹配则立即反转(贪心策略保证最优) - 反转代价统一:栈空时遇到
)反转代价为 \(1\),后续可与后面的)匹配;剩余(两两配对,每对需反转一个 - 平衡条件转化:最终栈中剩余 \(num2\) 个
(,说明有 \(num2\) 个(多余,由于总数平衡,必有 \(num2\) 个)也多余(已计入 \(num1\)),每对需一次反转 - 适用于"括号匹配 + 最小修改"类问题,核心是用栈模拟并统计不匹配情况
- 贪心匹配:遇到
【解题思路】

【算法标签】
普及- #栈
【代码详解】
#include <bits/stdc++.h>
using namespace std;
string s;
int num1, num2;
int main()
{
cin >> s; // 输入字符串s
stack<char> st; // 定义队列
for (int i=0; i<s.length(); i++) { // 遍历所有字符
if (s[i]=='(') { // 如果是左括号
st.push('('); // 就压入栈中
} else { // 如果是右括号
if (st.empty()) { // 如果栈为空
num1++; // 则调整计数器自增1
st.push('('); // 修改为左括号后压入栈
} else { // 如果不为空
st.pop(); // 则弹出栈顶的左括号
}
}
}
while (!st.empty()) { // 当栈不为空
st.pop(); // 统计栈中有多少左括号
num2++;
}
cout << num2/2+num1 << endl; // 栈中左括号中有一半需要修改为右括号,再加上之前统计的值
return 0;
}
【运行结果】
())(
2
浙公网安备 33010602011771号