zoukankan      html  css  js  c++  java
  • 动态规划详解

          其实根本就谈不上详解,应该说只是随便谈谈,真正能详解动态规划的又有几个人,所以,这个标题略显扯淡。
          前段时间一直在做关于数据结构的题,也算是对数据结构有了一定的了解,知道了有些数据结构的基本算法。现在刚刚开始接触动态规划,其实写这篇文章的初衷是一来锻炼一下自己的总结能力,二来也是希望通过这篇文章,来指引和我一样的初学者,废话不多说了,开始吧。
    一、01背包
          我最开始接触的有关动态规划的是01背包,这应该也是动态规划入门最好的了吧。 01背包是很简单的问题,当然也不乏一些变种让你绞尽脑汁也想不到,这里我们不讨论那些,我只说最简单的。
          假设有n种物品,每种都只有一个,第i种物品的体积为Vi,重量为Wi,选一些物品到一个容量为C的背包,使得背包内总物品的体积不超过C的情况下重量最大。
          因为每种东西只有放入背包和不放入两种状态,这也就是01的由来了。对于这个问题,你当然可以枚举所有的可能性,如果有n个物品的话,总共有2^n种可能性,如果数据大的话,普通计算机是不可能计算的,当然你可以借一台超级计算机,这是另外一种情况,不予讨论。
         我们可以换一种思考方法,对与第i件物品,我们比较把它放入和不放入背包中的重量比较,取最大值,这样我们就可以得到这样一个表达式 dp(V) = max(dp(V), dp(V-vi) + wi), 具体实现我们可以采用递归的方式。这样时间复杂度好会不会好很多,很明显不会,因为会重复计算好多次,举个简单例子,如果我们计算dp(6),在这个过程中我们用到了dp(3),而在计算dp(5)的过程中也用到了dp(3),这样这两个过程就会重复计算一次dp(3),想想数据量大的话该有多少重复啊。。
         关于这个重复计算的问题,我们只要在过程中记录这些结果就完全可以避免重复计算,还是上面的例子,我们在dp(6)中计算了dp(3),并且将dp(3)的结果保存了,在dp(5)中我们直接调用dp(3),就行了,这种方法被称为记忆化搜索,因为dp()这个函数你在一定程度上可以把它当做dfs()。
        虽然记忆话搜索就是动态规划的思想,不过这还不是最好的方法,我们完全可以把递归改成递推的方式,这样dp[V] = max(dp[V], dp[V-vi] + wi),这个表达式也被称为状态转移方程,这也是动态规划的核心,还有,一定要理解01背包这个方程,因为绝大多数状态转移方程是由它演变来的。
    我们不难写出递推的代码
    for (int i = 1; i <= n; i++)
    {
        for (int j = C; j >= v[i]; j--)
        {
            dp[i][j] = max(dp[i][j], dp[i-1][j-v[i]] + w[i]);
        }
    }

    时间复杂度为O(n*n),时间上我们没办法在优化了,但在空间上我们可以继续优化,我们可以把dp数组改成一维的,得到以下代码也是正确的,因为在求解的过会覆盖掉一部分,但覆盖之后的值却是该状态的最优解。
    for (int i = 1; i <= n; i++)
    {
        for (int j = C; j >= v[i]; j--)
        {
            dp[j] = max(dp[j], dp[j-v[i]] + w[i]);
        }
    }
    二、最长非降子序列
         这个也比较简单,也是基本的东西,不过越基本的越应该熟悉。
         我们让dp[i] 保存前i个数的最长非降子序列长度,每次计算以第i个数结尾的最长子序列的长度。状态转移方程就是dp[i] = max(dp[i],dp[j] + 1)。
    #include <stdio.h>
    #include <string.h>
    
    int a[100];
    int dp[100];
    int max(int a, int b)
    {
        return a > b ?a : b;
    }
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 0; j < i; j++)
            {
                if (a[i] > a[j])
                    dp[i] = max(dp[i],dp[j] + 1);
            }
        }
        printf("%d\n",dp[n]);
    }

    三、最长公共子序列
     

       此文章将持续更新。。。。。。

  • 相关阅读:
    python+requests+re匹配抓取猫眼上映电影信息
    Qt 5.12 LTS 部署
    Apache 日志记录相关设置
    php curl 相关知识
    Apache缓存相关配置
    Apache开启GZIP 压缩网页
    Apache 相关 mod_rewrite ,RewriteCond,{HTTP_HOST}
    Andriod you must restart adb and eclipse
    JDK 环境变量的配置
    http 协议详解
  • 原文地址:https://www.cnblogs.com/xindoo/p/3595142.html
Copyright © 2011-2022 走看看