Учитывая целое число H и строку s, которая содержит только символы ( и ) и правильно сформирована (каждая открывающая скобка имеет соответствующую закрывающую скобку, пары скобок правильно вложены), верните целое число, минимально указывающее, сколько скобок вам нужно перевернуть. получить действительную строку, глубина которой не превышает H.
Глубина строки в данном случае — это максимальный уровень вложенности скобок. Например, строка (()(())) имеет глубину, равную 3.
В целом, я думаю, мы можем применить жадный подход — просто перебирать строковые символы и при этом сохранять переменную, в которой хранится текущая глубина строки. Если в какой-то момент оно окажется выше H, измените текущую скобку. Кроме того, в случае, если глубина будет ниже нуля, также переверните символ.
Это код, который я написал, и он работает отлично:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, H;
cin >> n >> H;
string s;
cin >> s;
int depth = 0;
int ans = 0;
for (int i = 0; i < s.size(); ++i) {
if (s[i] == '(' && depth == H) {
s[i] = ')';
++ans;
}
else if (s[i] == ')' && depth == 0) {
s[i] = '(';
++ans;
}
if (s[i] == '(')
++depth;
else
--depth;
}
cout << ans;
}
Это была моя интуиция. Но я не могу понять, как я могу быть уверен, что конечная строка (после изменения определенных символов) всегда будет допустимой (круглые скобки вложены правильно и т. д.).
Во-первых, в какой-то момент мы придем к случаю, когда глубина превысит H. Здесь мы переворачиваем открывающую скобку на закрывающую. Предположим, мы сделали это по индексу i (при повторении s). Благодаря этой модификации у нас будет закрывающая скобка, которой в будущем не будет соответствующей скобки (некоторый индекс j где j > i). Может случиться так, что после этого глубина станет ниже нуля (поэтому мы поменяем закрывающую скобку на открывающую), но это не обязательно (и в итоге мы получим недопустимую строку).
На этом мои рассуждения заканчиваются. Для меня это кажется недействительным, но если код работает, значит я что-то пропустил.
Итак, как я могу быть уверен, что после выполнения всех этих операций я получу действительную строку?
Также прочтите В чем проблема с «использованием пространства имен std;»? и, пожалуйста, не делайте карго-культовых вещей вроде «ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);», которые никогда не пройдут проверку кода в любой профессиональной среде и (если вы действительно не знаете, что делаете) просто приведут к ошибкам.
Но я не могу понять, как я могу быть уверен, что окончательная строка (после изменения местами определенных символов) всегда будет допустимой (круглые скобки вложены правильно и т. д.). Запустите тысячи случайных тестовых случаев с известными ответами, а затем сравните ваши ответы. на известные ответы. Вы просите нас стать вашим фаззинговым двигателем
@Someprogrammerdude Это не совсем разумный совет. В случае проблем с большим объемом ввода программы с заданной временной сложностью могут превысить ограничение по времени без этих строк для ускорения ввода. От их удаления нет никакой пользы, хотя я согласен, что отвязывание cin не так уж и необходимо (и cout в любом случае не привязано по умолчанию).
@Unmitigated Я никогда не работал над каким-либо реальным профессиональным программным проектом, где синхронизация потоков ввода/вывода C и C++ когда-либо имела какое-либо значение. Но я работал над базами кода, куда какие-то придурки добавляли такие вещи, как в код ОП, и это вызывало настоящие ошибки, которые стоили реальных денег. Это «конкурентное кодирование», от которого просто нужно избавиться. Конечно, это существует по какой-то причине, и есть (несколько) случаев, когда это может иметь смысл, но это территория экспертов, и это явно не то, где мы находимся в настоящее время.
@JesperJuhl Пока компании задают технические вопросы, которые представляют собой упрощенные вопросы конкурентного программирования, это вряд ли исчезнет. А поскольку это проблема конкурентного программирования, удаление этих строк на самом деле не помогает (и может существенно ухудшить решение).
Это просто, правда. Как только вы достигли открывающейся скобки на глубине H, вам необходимо преобразовать фрагмент строки до соответствующей закрывающей скобки в )()()...()(, поскольку это единственный способ сохранить глубину на уровне H и не глубже. Я думаю, что ваш алгоритм делает именно это, но сформулирован таким образом, что это не делает это очевидным.
@IgorTandetnik "это единственный выход" нет.





Перефразируя вопрос, я думаю, вы просите доказать правильность вашей программы, а не «написать фаззер», как предлагалось в некоторых комментариях.
Вы можете думать о допустимой строке скобок следующим образом: для вашей строки скобок s длины n мы можем представить ее с помощью последовательности a. Пусть ai = +1, если i-й символ — (, и пусть ai = -1, если i-й символ — ). Теперь, когда s является допустимой последовательностью скобок тогда и только тогда, когда ∑ i=0k ai ≥ 0 для 0 ≤ k < n и что ∑ i=0n-1 ai = 0. (Подробнее см. Каталонское число и путь Дьюка.)
Таким образом, изменение ( на ) эквивалентно изменению +1 на -1. Следовательно, предположим, что вы меняете al = +1 на al = -1 (от ( до )) и am = -1 на am = +1 (от ) до () для некоторого l < m, тогда ∑ i=0k a i будет неизменным для всех k до l и после m. Что касается сумм между l и m, поскольку ваша программа по сути ищет первое m, где сумма ∑ i=0m ai становится отрицательной после того, как вы изменили ( на ) в точке l, они определенно не отрицательны. Следовательно, строка в квадратных скобках, созданная вашей программой, всегда действительна.
Что касается вашего беспокойства: «из-за этой модификации у нас в будущем будет закрывающая скобка, у которой не будет соответствующей скобки» и «может случиться так, что после этого глубина станет ниже нуля», на самом деле это не будет проблемой. Рассмотрим следующий пример:
( ( ( ) ( ) ) )
Ваше решение должно перевернуть жирные скобки, если я хочу, чтобы максимальная глубина была равна 2.
( ( ) ) ( ) ( )
Теперь обратите внимание на жирные скобки ниже, ранее это была пара скобок: ( ( ( ) ( ) ) )
Но самая левая скобка теперь будет выглядеть следующим образом:
( ( ) ) ( ) ( )
Поэтому вам не нужно беспокоиться о том, какие именно скобки соединены в пары, а достаточно лишь о том, чтобы каждая закрывающая скобка была в паре с некоторой открывающей скобкой.
Не связано, но выкиньте первые три строки функций
main. Они всего лишь часть карго-культа , который обычно никогда не используется в реальном мире.