草庐IT

动态规划一:线性动态规划(8596+17098+17099)

sigd 2024-04-13 原文

(一)前言

        线性结构是最常见也是最重要的一种数据结构,N个数据元素以有序的方式排列。访问线性结构一般采用由前至后的遍历方法。线性动态规划就是在线性数据的基础上,通过某种递推方式(状态转移方程)得到最终结构的一种规划算法。这是最简单也是最基础的动态规划算法,一般可分为一维线性规划或二维线性规划两大类。

(二)动态规划的概念

       动态规划英文原词为dynamic programming,规划一般就是指“求解最优”。规划问题并不是转化为“解方程组”的求解问题,而是把规划问题视为一个多阶段的决策问题,每个阶段的最佳状态作为下一个阶段的基础。

        每次决策依赖于当前状态,决策后又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的。这种多阶段最优化决策解决问题的过程就称为动态规划

(三)动态规划算法的设计

        要培养一种递归性思考问题的方式。对于问题规模N的问题,先思考下如果N=1是否有解,然后借助递归思想,去尝试能否通过比N规模小的问题集合中推导出N的结果。由于是线性结构,如果我们界定N的问题是区间[1,N]的话,比N规模小的问题区间[1,X]是区间[1,N]的一部分,也就是上面提及的子问题。动态规划简言之就是通过计算子问题的解,最终得到问题N的解。

设计一个动态规划算法,有以下四个步骤:

1、刻画一个最优解的结构特征。(最优解是什么?多数情况是线性序列的一个子序列)

2、递归地定义最优解的值。(子序列元素间限定或特殊关系,递归思想发现求解规律)

3、计算最优解的值,通常采用自底向上的方法。(定义状态转移方程,就是如何推导出dp[i])

4、利用计算出的信息构造一个最优解。(问题的最优解不一定是最后一个元素的解,思考下)

(四)线性动态规划

        本部分通过一些例题展示动态规划算法的四个步骤。

例题一:8596 最长上升子序列(优先做)

        序列 (1, 7, 3, 5, 9, 4, 8) 就有许多个上上子序列,比如(1, 7), (3, 4, 8) 等。
所有这些上升子序列中最长的长度为4,比如 (1, 3, 5, 8). 

(1)最优解结构特征。要得到子序列,序列元素不但升序,且元素数量最多。

(2)递归最优解的值。假定a[i]是上升序列最后一个元素,那么a[i]一定值最大,原始序列中在其左侧且值比其小的元素都可以作为其前驱。设定dp[i]为以a[i]为最后一个元素的最长上升子序列长度,那么可以由其左侧比a[i]小的元素的最长上升子序列,再加上a[i]得到新的上升子序列,找到一个最长的,即为dp[i]。

(3)计算最优解的方法。怎么计算dp[i]呢,根据上述分析,状态转移方程

dp[i]=max(dp[k])+1 (1<=k<=i-1&&a[i]>a[k]),注意dp[i]至少为1。

(4)怎么得到最优解。从样例就能发现最长上升子序列最后一个元素不一定是整个序列最后一个元素,因此在计算dp[i](最优值)时顺便统计下最优解。算法复杂度O(n^2)

#include <iostream>
using namespace std;
int main()
{int i,j,n,dp[100005],a[100005];
    while(cin>>n&&n)
    {
        int ans=0;
        for(i=1; i<=n; i++)
            cin>>a[i],dp[i]=1;
        for(i=1; i<=n; i++)
        {
            for(j=i+1; j<=n; j++)
            {/**< a[i]<a[j],状态转移方程的实现 */
                if(a[i]<a[j]&&dp[j]<dp[i]+1) 
                    dp[j]=dp[i]+1;
            }
            ans=max(ans,dp[i]);
        }
        cout<<ans<<endl;
    }return 0}

--------------------------------------------------------------------------------------------------------------------------

例题二:17098 广告牌最佳安放问题

有一条路从西向东M公里,是一段旅行的公路。在这段公路上放置n块广告牌,广告牌的地点:x1,x2,...,xn。如果你放一块广告牌在地点xi,就能获得ri的收益(ri>0)。
该地公路局规定:两块广告牌不能小于或等于5公里。现在请你挑选并安排这些广告牌放置地点,使得你的总收益在公路局规定的限制下达到最大。

(1)最优解结构特征。还是得到一个子序列,子序列任意两个广告牌距离大于5,且总收益最大。

(2)递归最优解的值。假定x[i]是序列最后一个元素,其左侧和其距离超过5的元素都可以作为其前驱。设定dp[i]为以x[i]为最后一个元素的子序列的最大收益,那么dp[i]可以由其左侧距离超过5的所有元素的最大收益推导出来。

(3)计算最优解的方法。根据上述分析,状态转移方程

dp[i]=max(dp[k])+r[i]   (1<=k<=i-1&&x[i]-x[k]>5),注意dp[i]至少为r[i]。

(4)怎么得到最优解。思考可得最大收益时最后一个广告牌不一定放广告,因此和上题一样可以在计算dp[i](最优值)时顺便更新下最优解。ans=max(ans,dp[i]);

#include <iostream>#include <cstring>#include <algorithm>
using namespace std;
int n,m,a[100005],b[100005],dp[100005];/**< dp[i],i位置放置最后一个广告牌 最大收益 */
int main()
{   /**< 本代码使用了一些小技巧 */
    ios::sync_with_stdio(0),cin.tie(0);
    int i,j,k;
    cin>>m>>n;
    for(i=1;i<=n;i++)
        cin>>a[i];
    a[n+1]=a[n]+6;/**< 额外添加一个a[n+1],这样设计,就不用计算dp[i]最大值了 */
    for(i=1;i<=n;i++) 
        cin>>b[i],dp[i]=b[i];
    /**< 这里有一个提升性能的小技巧,因为距离是5,而每个广告牌都是整数,所以如果两个广告牌
间隔数量超过11个广告牌,那么再往前计算就没有意义了,由此使得算法复杂度降为O(n) */
    for(i=2;i<=n+1;i++)
       for(j=i-1;j>=i-11&&j>=0;j--)
        if(a[i]-a[j]>5)
        dp[i]=max(dp[i],dp[j]+b[i]);
    cout<<dp[n+1];
    return 0;}

例题三:17099 周工作计划安排

你正在某信息公司管理一个项目工作团队,每周你必须选择一项工作让团队来承担。所有的工作分为高压的低压的两类:
(1)低压的,诸如,为小学生建立Web管理网站。低压的项目可以获得li万元。
(2)高压的,诸如,涉及国家秘密或大型商业机遇的项目。高压的项目可以获得hi万元。
且高压项目,必须在前一周用一周的时间来准备(前一周不做任何其他工作)。你作为项目管理者,每周,要选择一项高压的或低压的工作来做。
问题模型:给定一组值l1,l2,...,ln和h1,h2,...,hn,找出一个最大价值的最优计划。

(1)最优解结构特征。仍然是线性结构的一个子序列,子序列每个元素有两种状态,L和H(低压或高压),如果序列中某个元素是高压H,那么其前驱元素下标和其相差为2,也就是选择了a[i],那么就不能选择a[i-1]。

(2)递归最优解的值。假定i是最后一周,计算器最大值dp[i],如果第i周选择低压,前一周的最大值+本周Li,如果第i周选高压,前一周必须休息,前2周最大值+本周Hi

(3)计算最优解的方法。根据上述分析,状态转移方程

dp[i]=max(dp[i-1]+li,dp[i-2]+hi) ,注意边界,只有第二周才能开始做高压工作。

(4)怎么得到最优解。和前两个例题不同,显然最后一周的结果必为最优解。

#include <iostream>
using namespace std;
int n,low[10005],high[10005],dp[10005];
int main()
{ ios::sync_with_stdio(0),cin.tie(0);
    int i,j;
    cin>>n;
    for(i=1; i<=n; i++)
        cin>>low[i];
    for(i=1; i<=n; i++)
        cin>>high[i];
    dp[1]=low[1];/**< 第一周只能低压工作 */
    for(i=2; i<=n; i++)
        dp[i]=max(dp[i-1]+low[i],dp[i-2]+high[i]);
    cout<<dp[n];
    return 0;}

(五)拓展

        线性动态规划是最简单的动态规划问题,也是是理解动态规划算法基础。

读者可以自行参看以下题目,尝试按上述四个步骤分析和解决问题。

198. 打家劫舍

55. 跳跃游戏

121. 买卖股票的最佳时机

91. 解码方法

有关动态规划一:线性动态规划(8596+17098+17099)的更多相关文章

  1. ruby - 在 Ruby 中动态创建数组 - 2

    有没有办法在Ruby中动态创建数组?例如,假设我想遍历用户输入的书籍数组:books=gets.chomp用户输入:"TheGreatGatsby,CrimeandPunishment,Dracula,Fahrenheit451,PrideandPrejudice,SenseandSensibility,Slaughterhouse-Five,TheAdventuresofHuckleberryFinn"我把它变成一个数组:books_array=books.split(",")现在,对于用户输入的每一本书,我想用Ruby创建一个数组。伪代码来做到这一点:x=0books_array.

  2. ruby - 是否可以将 IRB 提示配置为动态更改? - 2

    我想在IRB中浏览文件系统并让提示更改以反射(reflect)当前工作目录,但我不知道如何在每个命令后进行提示更新。最终,我想在日常工作中更多地使用IRB,让bash溜走。我在我的.irbrc中试过这个:require'fileutils'includeFileUtilsIRB.conf[:PROMPT][:CUSTOM]={:PROMPT_N=>"\e[1m:\e[m",:PROMPT_I=>"\e[1m#{pwd}>\e[m",:PROMPT_S=>"FOO",:PROMPT_C=>"\e[1m#{pwd}>\e[m",:RETURN=>""}IRB.conf[:PROMPT_MO

  3. ruby-on-rails - carrierwave:在序列化动态属性上安装 uploader - 2

    首先,我使用的是rails3.1.3和来自master的carrierwavegithub仓库的分支。我使用after_init钩子(Hook)来确定基于属性的字段页面模型实例并为这些字段定义属性访问器将值存储在序列化哈希中(希望它清楚我是什么谈论)。这是我正在做的事情的精简版:classPage省略mount_uploader命令让我可以访问我想要的属性。但是当我安装uploader时出现错误消息说“nil类的未定义新方法”我在源代码中读到有方法read_uploader和扩展模块中的write_uploader。我如何必须覆盖这些来制作mount_uploader命令使用我的“虚拟

  4. ruby - 在 Ruby 中动态生成多维数组 - 2

    我正在尝试动态构建一个多维数组。我想要的基本上是这样的(为简单起见写出来):b=0test=[[]]test[b]这给了我错误:NoMethodError:undefinedmethod`test=[[],[],[]]而且它工作正常,但在我的实际使用中,我不会事先知道需要多少个数组。有一个更好的方法吗?谢谢 最佳答案 不需要像您正在使用的索引变量。只需将每个数组附加到您的test数组:irb>test=[]=>[]irb>test[["a","b","c"]]irb>test[["a","b","c"],["d","e","f"]]

  5. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  6. ruby - 动态方法链? - 2

    如何在对象上调用方法名称的嵌套哈希?例如,给定以下哈希:hash={:a=>{:b=>{:c=>:d}}}我想创建一个方法,给定上面的散列,执行以下操作:object.send(:a).send(:b).send(:c).send(:d)我的想法是我需要从一个未知的关联中获取一个特定的属性(这个方法不知道,但程序员知道)。我希望能够指定一个方法链来以嵌套哈希的形式检索该属性。例如:hash={:manufacturer=>{:addresses=>{:first=>:postal_code}}}car.execute_method_hash(hash)=>90210

  7. ruby - 如何使用 method_missing 动态声明方法? - 2

    我有一个ruby​​程序,我想接受用户创建的方法,并使用该名称创建一个新方法。我试过这个:defmethod_missing(meth,*args,&block)name=meth.to_sclass我收到以下错误:`define_method':interningemptystring(ArgumentError)in'method_missing'有什么想法吗?谢谢。编辑:我以不同的方式让它工作,但我仍然很好奇如何以这种方式做到这一点。这是我的代码:defmethod_missing(meth,*args,&block)Adder.class_evaldodefine_method

  8. ruby - 动态扩展现有方法或覆盖 ruby​​ 中的发送方法 - 2

    假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入

  9. ruby - 使用 jbuilder 创建具有动态哈希键的 JSON - 2

    这里我想输出带有动态组名的json而不是单词组@tickets.eachdo|group,v|json.group{json.array!vdo|ticket|json.partial!'tickets/ticket',ticket:ticketend}end@ticket是这样的散列{a:[....],b:[.....]}我想要这样的输出{a:[.....],b:[....]} 最佳答案 感谢@AntarrByrd,这个问题有类似的答案:JBuilderdynamickeysformodelattributes使用上面的逻辑我已经

  10. ruby - 在 Rakefile 中动态生成 Rake 测试任务(基于现有的测试文件) - 2

    我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n

随机推荐