Динамічне програмування дає вам можливість подумати про розробку алгоритму. Це часто дуже корисно.
Методи запам'ятовування та знизу вгору дають вам правило / метод перетворення відношень повторення в код. Пам'ять - це досить проста ідея, але найкращі ідеї часто є!
Динамічне програмування дає вам структурований спосіб продумати час роботи вашого алгоритму. Час виконання в основному визначається двома числами: кількістю підпроблем, які ви повинні вирішити, і часу, необхідного для вирішення кожної підпрограми. Це забезпечує зручний простий спосіб подумати над проблемою проектування алгоритму. Коли у вас є співвідношення рецидивів кандидата, ви можете подивитися на це і дуже швидко зрозуміти, яким може бути час роботи (наприклад, ви часто дуже швидко можете сказати, скільки буде підпроблем, що є нижньою межею на час роботи; якщо є багато експонентних підпроблем, які вам доведеться вирішити, то повтор, ймовірно, не буде хорошим підходом). Це також допомагає виключити декомпозиції кандидатських субпроблем. Наприклад, якщо у нас є рядок , визначаючи підпроблему префіксом S [ 1 .. i ] або суфіксом S [ j . . n ] або підряд S [ i . . j ] може бути розумним (кількість підпроблем є поліноміальним в n ), але визначення підпроблеми за допомогою послідовності S , ймовірно, не буде хорошим підходом (кількість підпроблем є експоненціальною в n ). Це дозволяє обрізати "простір пошуку" можливих повторень.S[1..n]S[1..i]S[j..n]S[i..j]nSn
Динамічне програмування дає вам структурований підхід до пошуку рецидивів кандидатів. Емпірично такий підхід часто ефективний. Зокрема, є деякі евристики / загальні шаблони, які ви можете розпізнати для загальних способів визначення підпрограм, залежно від типу введення. Наприклад:
Якщо вхід є натуральним числом , одним із варіантів способу визначення підпрограми є заміна n меншим цілим числом n ' (st 0 ≤ n ′ ≤ n ).nnn′0≤n′≤n
Якщо вхідним рядком є рядок , деякі можливі способи визначення підпроблеми включають: замінити S [ 1 .. n ] префіксом S [ 1 .. i ] ; замініть S [ 1 .. n ] суфіксом S [ j . . п ] ; замініть S [ 1 .. n ] на підрядку S [ i . . j ]S[ 1 .. н ]S[ 1 .. н ]S[ 1 .. я ]S[ 1 .. н ]S[ j . . n ]S[ 1 .. н ]S[ i . . j ]. (Тут підпроблема визначається вибором .)i , j
Якщо вхід є списком , зробіть те саме, що ви зробили для рядка.
Якщо вхід - це дерево , то одним із варіантів способу визначення підпрограми є заміна T будь-яким піддіревом T (тобто, вибір вузла x і заміна T на піддерево, укорінене в x ; підпроблема визначається вибором x ).TTTxTxx
Якщо вхід - пара , то рекурсивно подивіться на тип x та тип y, щоб визначити спосіб вибору підпроблеми для кожного. Іншими словами, одним із можливих способів визначення підпроблеми є заміна ( x , y ) на ( x ′ , y ′ ), де x ′ є підпроблемою для x, а y ′ є підпроблемою для y . (Ви також можете розглянути підпроблеми форми ( x , y(x,y)xy(x,y)(x′,y′)x′xy′y Або ( x ′ , y ) .)(x,y′)(x′,y)
І так далі. Це дає вам дуже корисну евристику: просто переглянувши типовий підпис методу, ви можете скласти список кандидатських способів визначення підпроблем. Іншими словами, просто дивлячись на постановку проблеми - дивлячись лише на типи входів - ви можете придумати кілька кандидатських способів визначення підпроблеми.
Це часто дуже корисно. Це не говорить вам про те, що таке відношення рецидивів, але коли у вас є певний вибір, як визначити підпроблему, часто не дуже важко розробити відповідне відношення рецидиву. Таким чином, це часто перетворює проектування алгоритму динамічного програмування в структурований досвід. Ви записуєте на металобрухт список кандидатських способів визначення підпроблем (використовуючи вищевказані евристики). Потім для кожного кандидата ви намагаєтеся записати відношення повторення та оцінюєте його час роботи, підраховуючи кількість підпроблем та час, витрачений на підпроблему. Випробувавши кожного кандидата, ви зберігаєте найкраще, що вам вдалося знайти. Надання певної структури процесу розробки алгоритму є головною підмогою, оскільки в іншому випадку дизайн алгоритму може залякати (там '