【刷题版】掌握算法的一揽子计划——动态规划总结

动态规划是一种通过将原问题分解为相对简单的子问题来求解,然后将子问题的解存储起来避免之后重复计算,并最终将子问题组合成原问题的解决方法。动态规划并不算是一种具体的算法,更应该被认为是一种解决问题的思想。

动态规划通常适用于具有重叠子问题和最优子结构性质的问题。具有重叠子问题的问题意味着在解决问题的过程中,我们需要解决相同的子问题。而最优子结构性质意味着问题的最优解可以通过其子问题的最优解来计算。

能用动态规划解决的问题,需要满足三个条件:最优子结构,无后效性和子问题重叠。

  • 最优子结构:假设问题的最优解所包括的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
  • 无后效性:即某阶段状态一旦确定。就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响曾经的状态。仅仅与当前状态有关。
  • 子问题重叠:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到(该性质并非动态规划适用的必要条件,可是假设没有这条性质。动态规划算法同其它算法相比就不具备优势)。

动态规划算法的具体实现方法有多种,例如自顶向下的记忆化搜索和自底向上的迭代算法。在实际应用中,动态规划可以用来解决一些经典问题,如最长公共子序列问题、背包问题和图的最短路径问题等。

本文主要举一些简单和常见的例子。

线性 DP

线性DP是动态规划问题中的一类问题,指状态之间有线性关系的动态规划问题。也可以是多维的线性问题。

常见问题:

  • 数字金字塔
  • 最长上升子序列
  • 最长不下降子序列
    • 与上一题相比,需要输出一个符合条件的解,可以用一个额外的数组记录以每一个元素结尾时该元素所对应的最长不下降子序列的长度,然后逆序遍历每一个元素,记录符合条件的数据。浅浅贴一个代码 ↓ \downarrow
#include <iostream>
using namespace std;

const int N = 200 + 5;
// dp 保存第 i 个数字结尾的子序列的长度
// sc 保存原始数据
// tp 保存临时序列
int dp[N], sc[N], tp[N];

// 从 s 到 e 搜索 第一个 大于 tar 的元素的下标
int bin_search(const int * nums, int s, int e, int tar) {
    if (s > e) return 0;

    int l = s, r = e;
    while (l <= r) {
        int mid = (l + r) >> 1;

        if (nums[mid] <= tar)
            l = mid + 1;
        else
            r = mid - 1;
    }

    return l;
}

int main() {
    int n;
    cin >> n;

    int idx = 0;
    for (int i = 0; i < n; ++i) {
        scanf("%d", &sc[i]);
        int k = bin_search(tp, 0, idx - 1, sc[i]);
        
        if (k == idx) {
            tp[idx++] = sc[i];
        } else {
            tp[k] = sc[i];
        }

        dp[i] = k + 1;
    }

    cout << "max=" << idx << endl;

    int ans[idx + 5], len = idx;
    for (int i = n - 1; i >= 0; --i) {
        if (dp[i] == len)
            ans[len--] = sc[i];
    }

    for (int i = 1; i <= idx; ++i) {
        printf("%d ", ans[i]);
    }
}
  • 一个应用:导弹拦截
    • 在这个问题中,第一问求,最多可以拦截的导弹数,按题意,每一发炮弹都不能高于前一发的高度,此处只需要求一个最长不上升子序列就好了
    • 第二问中,求最少需要多少套设备,正常想最少需要,那么我们可以每套设备尽可能拦截多的导弹,直至全部拦截完为止,但若求多次最长不上升子序列很麻烦。这有一个 trick 是,我们可以发现按最初的求法,每次求出的序列和之前的序列一定会有一个上升的过程,不然就可以归到前一个子序列里边了。所以第二问的解就是同时求一个最长严格上升子序列即可。
  • 最长公共子序列
  • 最大子序和
  • 最大上升子序和
  • 编辑距离

背包问题

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。这个问题直接看背包九讲就好了

常见问题:

参考资料

  • https://oi-wiki.org/dp
  • https://blog.csdn.net/qq_38670588/article/details/108186884
  • https://baike.baidu.com/item/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98/2416931?fr=aladdin

猜你喜欢

转载自blog.csdn.net/weixin_43786143/article/details/129658274
今日推荐