Изменения

Перейти к: навигация, поиск

Задача о расстановке знаков в выражении

6597 байт добавлено, 01:03, 5 июня 2017
Реализация
'''{{ Задача о расстановке знаков в выражении''' — задача о нахождении максимального значения выражения, полученного расстановкой знаков |definition = Пусть задана последовательность неотрицательных чисел <tex dpi = "120130">+a_1, a_2, \ldots, a_n \ (a_i \timesgeqslant 0)</tex> и скобок в последовательности . Требуется расставить знаки <tex dpi = "130120">a_1, a_2+, \dots, a_n \ (a_i \ge 0)times</tex>и скобки в последовательности, чтобы значение полученного арифметического выражения было максимальным.}}
== Решение ==
Данная задача решается с использованием [[Динамическое_программирование | принципа оптимальности на подотрезке[http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5#.D0.9F.D1.80.D0.B8.D0.BD.D1.86.D0.B8.D0.BF_.D0.BE.D0.BF.D1.82.D0.B8.D0.BC.D0.B0.D0.BB.D1.8C.D0.BD.D0.BE.D1.81.D1.82.D0.B8_.D0.BD.D0.B0_.D0.BF.D0.BE.D0.B4.D0.BE.D1.82.D1.80.D0.B5.D0.B7.D0.BA.D0.B0.D1.85подотрезках]]. Введём матрицу <tex>d</tex> размером <tex>n \times n</tex>, где <tex>d[i][j]</tex> будет равен максимальному значению, достигаемому на подотрезке <tex dpi = "130">a_i, a_{i+1}, \dotsldots, a_j</tex>.
Получаем следующие соотношения: <br />
* <tex>d[i][i] = a_i </tex><br />
* <tex>d[i][j] = \max\limits_{\mathop{k = i..j-1}}[\max(d[i][k]+d[k+1][j], d[i][k] \times cdot d[k+1][j])] \ (i < j)</tex> <br />
Вычислим элементы таблицы <tex>d</tex>, тогда ответом на задачу будет значение <tex>d[1][n]</tex>.
 
== Доказательство ==
Рассмотрим ее, например, в такой формулировке. В арифметическом выражении, операндами которого являются целые числа, а операциями — бинарные арифметические операции <tex>+</tex> и “*”<tex>\times</tex>, расставить скобки так, чтобы результат оказался максимальным.Подзадачами, через которые мы будем выражать оптимальное решение, будут задачи об оптимальной расстановке скобок в произведениях наших операндов, начиная с <tex>i</tex>-го и заканчивая <tex>j</tex>-м. Запоминать результат оптимального решения соответствующей подзадачи мы будем в элементе <tex>d[i, j] </tex> матрицы, размером <tex>n*\times n</tex>, диагональные элементы которой <tex>(d[i, i]) </tex> равны операндам, а для <tex>i > j </tex> все элементы равны <tex>0</tex>. Пусть мы хотим подсчитать значение арифметического выражения для операндов, начиная с <tex>i</tex>-го и заканчивая <tex>j</tex>-м для <tex>i < j</tex>. Если мы предположим, что последней будет выполняться арифметическая операция, расположенная после операнда с номером <tex>k (k < j) </tex> и эта операция — сложение, то результат подсчета будет равен сумме элементов <tex>d[i, k] </tex> и <tex>d[k+1, j] </tex> нашей матрицы. С умножением ситуация несколько более сложная. Так как операндами могут быть и отрицательные числа, то для нахождения максимума из произведений нужно знать не только максимальные, но и минимальные значения для арифметических операций над операндами с <tex>i</tex>-го по <tex>k</tex>-й и с <tex>k+1</tex>-го по <tex>j</tex>-й соответственно. Значит для любой последовательности операндов нужно хранить значение как максимально, так и минимально возможного результата выполнения арифметических операций над ними. Однако, дополнительная матрица для хранения минимальных значений не нужна. Так как для <tex>i > j </tex> матрица <tex>d </tex> не заполнена, мы можем отвести эти элементы для хранения минимумов, только запоминать результат решения этой подзадачи для операндов, начиная с <tex>i</tex>-го и заканчивая <tex>j</tex>-м, мы будем в элементе <tex>d[j, i]</tex>. Тогда в общем случае, если после k-го операнда стоит операция умножения, то максимальный результат будет равен <tex>max(d[i, k] * d[k+1, j], max(d[k, i] * d[j, k] +1], d[i, k+1] * d[j, k+1], d[i][k, i] * \cdot d[k+1, ][j]).)</tex>;
== Реализация ==
<font color = "green">// d - матрица как в описании; a - заданная последовательность из n элементов; i, j, k - счётчики</font>
'''int maxValueOfExpression'''(a, n): '''for ''' i := 1 '''to ''' n do: d[i][i] := a[i];
'''for ''' i := n - 1 '''downto ''' 1 do: '''for ''' j := i + 1 '''to ''' n do: '''for ''' k := i '''to ''' j - 1 do: d[i][j] := max(d[i][j], max(d[i][k] + d[k+1][j], d[i][k] * d[k+1][j])); write( '''return''' d[1][n]); // вывод ответа 
== Пример ==
Пусть дана последовательность <tex>2, 1, 1, 2</tex>. Таблица <tex>d</tex> для неё будет выглядеть так: :{| border = 1; class="wikitable" cellpadding|-align ="center"|&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;<tex>j = 1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>j = 2</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>j = 3</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>j = 4</tex>&nbsp;&nbsp;|-align = " bordercenter"|&nbsp;&nbsp;<tex>i = 1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>2</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>3</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>4</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>9</tex>&nbsp;&nbsp;|-align ="center"|&nbsp;&nbsp;<tex>i = 2</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>0</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>2</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>4</tex>&nbsp;&nbsp;|-align = " stylecenter"|&nbsp;&nbsp;<tex>i = 3</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>0</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>0</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>3</tex>&nbsp;&nbsp;|-align ="bordercenter"|&nbsp;&nbsp;<tex>i = 4</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>0</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>0</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>0</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>2</tex>&nbsp;&nbsp;|} == Восстановление ответа ==С помощью описанного выше алгоритма можно восстановить оптимальное арифметическое выражение. Для этого нужно в таблице <tex>d</tex> хранить каким способом был получен оптимальный ответ. То есть будем хранить <tex>index</tex> {{---}} границу раздела на 2 подотрезка <tex>(i \ldots index</tex> и <tex>index + 1 \ldots j)</tex> и <tex>operation</tex> {{---collapse}} операцию совершенную над двумя отрезками (<tex>+, \times</tex>). В итоге для каждого отрезка мы будем знать какой знак и куда поставить. Для простоты будем заключать каждый полученный подотрезок в скобки. Таким образом мы получим выражение, в котором полностью расставлены скобки. Для вышеприведенного примера таблица будет выглядеть так: collapse :{| border = 1;class="wikitable"|-align = "center"| &nbsp;&nbsp;<tex>index</tex>&nbsp;&nbsp;|| &nbsp;&nbsp;<tex>j = 1 </tex>&nbsp;&nbsp;|| &nbsp;&nbsp;<tex>j = 2 </tex>&nbsp;&nbsp;|| &nbsp;&nbsp;<tex>j = 3 </tex>&nbsp;&nbsp;|| &nbsp;&nbsp;<tex>j = 4</tex>&nbsp;&nbsp;|-align = "center"| &nbsp;&nbsp;<tex>i = 1 </tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>2</tex>&nbsp;&nbsp;|-align = "center"|&nbsp;&nbsp;<tex>i = 2</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;| &nbsp;&nbsp;<tex>2 </tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>3</tex>&nbsp;&nbsp;| -align = "center"|&nbsp;&nbsp;<tex>i = 3 </tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;| 4 &nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;| 9&nbsp;&nbsp;<tex>3</tex>&nbsp;&nbsp;|-align = "center"| &nbsp;&nbsp;<tex>i = 2 4</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;| 0 &nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>-</tex>&nbsp;&nbsp;|} :{| border = 1 ; class="wikitable"|-align = "center"|&nbsp;&nbsp;<tex>operation</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>j = 1</tex>&nbsp;&nbsp;| &nbsp;&nbsp;<tex>j = 2 </tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>j = 3</tex>&nbsp;&nbsp;| &nbsp;&nbsp;<tex>j = 4</tex>&nbsp;&nbsp;|-align = "center"| &nbsp;&nbsp;<tex>i = 3 1</tex>&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;| 0 &nbsp;&nbsp;<tex>+</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>\times</tex>&nbsp;&nbsp;| 0 &nbsp;&nbsp;<tex>\times</tex>&nbsp;&nbsp;|-align = "center"|&nbsp;&nbsp;<tex>i = 2</tex>&nbsp;&nbsp;| 1 &nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;| 3&nbsp;&nbsp;<tex>+</tex>&nbsp;&nbsp;|&nbsp;&nbsp;<tex>\times</tex>&nbsp;&nbsp;|-align = "center"| &nbsp;&nbsp;<tex>i = 4 3</tex>&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;| 0 &nbsp;&nbsp;<tex>+</tex>&nbsp;&nbsp;|-align = "center"| 0 &nbsp;&nbsp;<tex>i = 4</tex>&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;| 0 &nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;| 2&nbsp;&nbsp;&nbsp;&nbsp;
|}
После того, как посчитана динамика, можно рекурсивно восстановить ответ:*Если длина текущего отрезка равна <tex> 1 </tex>, выходим из рекурсии*Оборачиваем текущий отрезок <tex> l \ldots r </tex> в скобки*Ставим после элемента последовательности на позиции <tex> d[l][r].index </tex> знак <tex> d[l][r].operation </tex>*Рекурсивно запускаем от отрезков <tex> l \ldots d[l][r].index </tex> и <tex> d[l][r].index + 1 \ldots r </tex>  Таким образом ответ <tex> ((2+1)\times(1+2))</tex> == Источники Решение задачи без возможности использования скобок ==* КорменПри условии задачи, в котором запрещено использовать скобки, Твышеописанный алгоритм будет работать некорректно.Дело в том, Лейзерсончто если на отрезке <tex>k\ldots j</tex> была использована операция сложения, Что мы не можем перемножить результаты на отрезке <tex>i\ldots k - 1</tex> и отрезке <tex>k\ldots j</tex>.Для решения этой проблемы будем использовать две матрицы <tex>d</tex> {{---}} такую же как и раньше, Ривеста также <tex>p</tex> <tex>(p[i][j]</tex> {{---}} произведение чисел <tex dpi = "130">a_i, Р.a_{i+1}, Штайн\ldots, Кa_j)</tex>. Тогда получим новую формулу пересчета динамики:<br />* <tex>d[i][j] = \max\limits_{\mathop{k = i. Глава 15. Динамическое программирование j-1}}[\max(d[i][k] + d[k + 1][j], p[i][k] \cdot p[k + 1][j])] \ (i < j)</tex> <br / Алгоритмы> == См. также ==*[[Задача о порядке перемножения матриц | Задача о порядке перемножения матриц]]*[[Задача о наибольшей общей подпоследовательности | Задача о наибольшей общей подпоследовательности]] == Источники информации==*[http: построение //fvn2009.narod.ru/Olympiads/Rules_Olympiads/Rules22.htm Олимпиадные задачи и анализ = Introduction to Algorithms / Под редрешения {{---}} Классические задачи динамического программирования]* Томас Х. Кормен, Чарльз И. В. КрасиковаЛейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд. - 300стр. — М.: Вильямс«Вильямс», 20052007. — 1296 с. 459. — ISBN 5-84598489-0857-4*
[[Категория:Дискретная математика и алгоритмы]]
[[Категория:Динамическое программирование]]
14
правок

Навигация