Алгоритм динамического программирования N, K задача - PullRequest
7 голосов
/ 11 февраля 2010

Алгоритм, который возьмет два положительных числа N и K и вычислит максимально возможное число, которое мы можем получить, преобразовав N в другое число, удалив K цифр из N.

Например, допустим, у нас N = 12345 и K = 3, поэтому наибольшее возможное число, которое мы можем получить, удалив 3 цифры из N, равно 45 (другие преобразования будут 12, 15, 35, но 45 - самое большое). Также вы не можете изменить порядок цифр в N (так что 54 НЕ является решением). Другим примером будет N = 66621542 и K = 3, поэтому решение будет 66654.

Я знаю, что это проблема, связанная с динамическим программированием, и я не могу понять, как ее решить. Мне нужно решить это за 2 дня, поэтому любая помощь приветствуется. Если вы не хотите решать эту проблему для меня, вам не нужно, но, пожалуйста, укажите мне на хитрость или хотя бы некоторые материалы, где я могу прочитать больше о некоторых подобных проблемах.

Заранее спасибо.

Ответы [ 5 ]

6 голосов
/ 11 февраля 2010

Это можно решить в O (L), где L = количество цифр. Зачем использовать сложные формулы DP, когда мы можем использовать для этого стек:

Для: 66621542 Добавьте цифру в стек, когда в стеке меньше или равно L - K цифр: 66621. Теперь удалите цифры из стека, пока они меньше, чем текущая прочитанная цифра, и поместите текущую цифру в стек:

читать 5: 5> 2, вытолкнуть 1 из стека. 5> 2, поп 2 также. положить 5: 6665 прочитайте 4: стек не заполнен, поместите 4: 66654 читать 2: 2 <4, ничего не делать. </p>

Вам нужно еще одно условие: будьте внимательны, чтобы не вытолкнуть больше элементов из стека, чем осталось число в вашем номере, иначе ваше решение будет неполным!

Другой пример: 12345 L = 5, K = 3 положить в стек L - K = 2 цифры: 12

прочитайте 3, 3> 2, поп 2, 3> 1, поп 1, положите 3. стек: 3 читать 4, 4> 3, поп 3, положить 4: 4 прочитайте 5: 5> 4, но мы не можем выдвинуть 4, иначе у нас не останется достаточно цифр. так что нажимай 5:45.

5 голосов
/ 11 февраля 2010

Ну, чтобы решить любую проблему динамического программирования, вам нужно разбить ее на повторяющиеся подрешения.

Скажем, мы определили вашу проблему как A (n, k), которая возвращает наибольшее возможное число, удаляя k цифр из n.

Из этого можно определить простой рекурсивный алгоритм.

Используя ваш пример, A (12345, 3) = max {A (2345, 2), A (1345, 2), A (1245, 2), A (1234, 2)}

В общем, A (n, k) = max {A (n с удаленной 1 цифрой, k - 1)}

И ваш базовый случай: A (n, 0) = n.

Используя этот подход, вы можете создать таблицу, которая кэширует значения n и k.

int A(int n, int k)
{
  typedef std::pair<int, int> input;
  static std::map<input, int> cache;

  if (k == 0) return n;

  input i(n, k);
  if (cache.find(i) != cache.end())
    return cache[i];

  cache[i] = /* ... as above ... */

  return cache[i];
}

Теперь это прямое решение, но есть лучшее решение, которое работает с очень маленьким одномерным кэшем. Попробуйте перефразировать вопрос следующим образом: «Учитывая строку n и целое число k, найдите лексикографически наибольшую подпоследовательность в n длины k». По сути, это ваша проблема, а решение гораздо проще.

Теперь мы можем определить другую функцию B (i, j) , которая дает наибольшую лексикографическую последовательность длины (i - j) , используя только первую i цифр n (другими словами, удалив j цифр из первых i цифр n ).

Снова используя ваш пример, мы получили бы:

B (1, 0) = 1

B (2, 0) = 12

B (3, 0) = 123

B (3, 1) = 23

B (3, 2) = 3

и т.д.

Немного подумав, мы можем найти отношение повторения:

B (i, j) = макс. (10B (i-1, j) + n i , B (i-1, j-1))

или, если j = i , то B (i, j) = B (i-1, j-1)

и B (0, 0) = 0

И вы можете закодировать это так же, как описано выше.

4 голосов
/ 11 февраля 2010

Хитрость в решении проблемы динамического программирования, как правило, заключается в том, чтобы выяснить, как выглядит структура решения, и более конкретно, если оно имеет оптимальную подструктуру .

В этом случае мне кажется, что оптимальное решение с N = 12345 и K = 3 будет иметь оптимальное решение для N = 12345 и K = 2 как часть решения. Если вы можете убедить себя в том, что это так, то вы сможете рекурсивно выразить решение проблемы. Затем выполните это с запоминанием или снизу вверх.

1 голос
/ 11 февраля 2010

Два наиболее важных элемента любого динамического программного решения:

  1. Определение правильных подзадач
  2. Определение рекуррентного отношения между ответом на подзадачу и ответом на более мелкие подзадачи
  3. Нахождение базовых случаев , наименьших подзадач, ответ на которые не зависит ни от каких других ответов
  4. Определение порядка сканирования , в котором вы должны решить подзадачи (чтобы вы никогда не использовали рекуррентное отношение на основе неинициализированных данных )

Вы будете знать, что у вас есть правильные подзадачи, определенные, когда

  • Проблема, на которую вам нужно ответить - одна из них
  • Базовые случаи действительно тривиальны
  • Рецидив легко оценить
  • Порядок сканирования прост

В вашем случае просто указать подзадачи. Поскольку это, вероятно, домашнее задание, я просто дам вам подсказку, что вы можете пожелать, чтобы N имел меньше цифр для начала с .

0 голосов
/ 11 февраля 2010

Вот что я думаю:

Рассмотрим первые k + 1 цифр слева. Найдите самый большой, найдите его и уберите числа слева. Если существует два одинаковых самых больших числа, найдите самое левое и удалите числа слева от этого. сохранить количество удаленных цифр (назовите его j).

Проделайте то же самое с новым числом как N и k + 1-j, как и с K. Делайте это до тех пор, пока k + 1 -j не станет равным 1 (надеюсь, будет, если я не ошибаюсь)

Номер, который вы в итоге получите, будет номером, который вы ищете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...