如何掌握动态规划的套路

如何掌握动态规划的套路

动态规划(Dynamic Programming),简称DP,这个名字给人的感觉是一种非常高大上非常复杂的算法,很多同学看到这个名字可能就会望而却步,在面试的时候也非常害怕被问到动态规划的题目。实际上,它并不是不是一种确定的算法,它是一种最优化的方法求解问题的思想或方法。它是由美国数学家贝尔曼(Bellman)在研究多阶段决策过程的优化问题时提出。不过,与之对应的还有一些与时间无关的静态规划,如:线性规划、非线性规划等。在运筹学中,动态规划是的非常重要的内容,在各个行业领域都有着广泛的应用。我们如何理解动态规划?

如果一个问题的最优解可以通过其子问题的最优解经过推导得到,那么,我们就可以先求出其子问题的最优解,根据子问题的解得出原问题的最优解。如果子问题有较多的重复出现,为了减少重复计算,降低时间复杂度,则可以自底向上从最终子问题向原问题逐步求解并先将子问题存储起来,在求解大的子问题时可以直接从表中查询子问题的解,这就是动态规划的基本思想。

简单来来理解就是将一个大问题简化成若干子问题,并存入一个表中,再根据数据表中子问题的解求出大问题的解。这种算法看上去是不是很熟悉?其实,动态规划和分治算法类似,我们也常常将其和分治算法进行比较。它们都需要将其分解成若干子问题并求解子问题。不同的是分治算法是自顶向下求解各子问题,然后合并子问题的解从而得到原问题的解;而动态规划是将子问题拆解之后,自底向上求解子问题的解并将存储结果存储起来,在求解大的子问题时直接查询子问题的解,算法效率也将大大的提高。理论描述太过生硬和枯燥,我们直接来看一个例子

理论描述太过生硬和枯燥,我们直接来看一个例子。

斐波那契数列

在这里插入图片描述
斐波那契数列是一个非常神奇的数列,它由意大利数学家莱昂纳-斐波那契提出,其特征是数列某一项的值是前两项的和,也可以称作黄金分割数列。

在这里插入图片描述

我们可以用下面的通项公式来表示斐波那契数列。从斐波那契数列的公式中可知,数列的第n(n>2)项的值f(n)等于f(n)+f(n-1),如果要求得f(n)值就需要先求得f(n-1)和f(n-2)的值,为了便于分析,我们当假设n=6,我们可以按照下图进行分解,一步步分解成小的值。
在这里插入图片描述

int fib(int n)
{
    if(n==1 || n==2) return 1;
    return fib(n-1) + fib(n-2);
}

但是,很明显这种算法是指数时间复杂度O(2^n),其复杂度会随着n的增加成指数增长,当n取到一定大时,将需要很长的时间,显然这不是一种最优的算法。不过,仔细观察上图的各个分解项,我们会发现图中有很多重复的子项,这就是上面这种递归算法复杂度较高的原因。那么,还能不能进行优化呢?答案是肯定的。我们可以通过动态规划的思想来优化上面这个算法,为了避免大量的重复计算,我们可以从最底层的子问题开始计算,并通过一个表来存储这些子问题的值,当再次遇到这个值就不需要再重新计算。如下面的程序,我们从最小的子问题n=1,2开始向上计算,并且定义了一个vector容器用来存储被计算过的子问题的值,下次再计算大问题时直接调用容器里的值即可。

int fib(int n)
{
    vector<int> dp(n, 0);

    dp[0] = dp[1] = 1;
    for (int i = 2; i < n; i++)
    {
        dp[i] = dp[i - 1] + dp[i - 2];
    }

    return dp[n-1];
}

很明显上面的这种算法,大大降低了算法的时间复杂度,现在的时间复杂度就是O(n)了。不过,虽然时间复杂度降低了,这却是牺牲了空间换取过来的。实际上我们还可以进一步去优化,从公式上我们分析可以看出,要求出某一项的值我们需要先求出其前两项子问题的值,当我们自下而上求解子问题的过程中,我们直接保存连续两项子问题的值即可。

int fib(int n)
{
    int dp[2]={1,1};

    for (int i = 2; i < n; i++)
    {
        int tmp = dp[0];
        dp[0] = dp[1];
        dp[1] = dp[1] + tmp;
    }

    return dp[1];
}

这是我看的很好的一篇文章就把一部分粘过来了
动态规划的套路

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值