程序设计方法与实践-动态规划

西北工业大学NOJ 2023 C++程序设计实战指南:从基础到竞赛 本文详细介绍了西北工业大学NOJ在线评测系统在C++程序设计学习与竞赛准备中的实战应用。从基础语法到算法优化,再到团队竞赛训练,NOJ系统通过严格的评测机制和阶梯式题目设计,帮助编程学习者全面提升C++编程能力和竞赛水平。 阅读详情

什么是动态规划?

  • 动态规划是一种算法设计技术,是解决多阶段最优决策问题的通用方法。
  • 如果问题是由多个子问题组成,那么就可以使用动态规划技术解决。关键是要找到子问题解与原问题解的关系。
  • 这样的子问题会出现在求解原问题的递推关系中,这个递推关系中一定要包含更小问题的解。这样才能在递推的过程中,不断将问题解决。

这里举一个简单的例子,以斐波那契函数的递归求解过程为例:

如上图,是递归解决Fibonnacci(斐波那契数l列)问题的部分过程,不难发现,图中相同背景颜色的式子将会被重复计算,并且越往下会被重复计算更多次,这无疑大大增加了时间的复杂性。

所以我们的思路就是,与其重复地对子问题计算多次,不如只将子问题求解一次,然后记录在表中,然后然后不断迭代,最后我们可以从表中得到原问题的解。

最优性原理

  • 无论初始状态和初始决策是什么样的,其余的决策必须相对于初始决策产生的状态构成一个最优决策序列。
  • 一个问题实例的最优解一定是该问题子实例的最优解组成的。
  • 一般来说,如果一个问题能够满足最优性原理,那么该问题就能够使用动态规划技术解决。那么解决问题的关键就是找到各阶段的递推关系式。

动态规划通用步骤

  • 划分阶段:根据问题的时间或空间特征,将问题划分成若干个阶段(一般是根据规模大小)。
  • 选择状态:将问题发展到的各个阶段使用客观的状态方程标志出来。
  • 确定决策并写出状态转移方程:就是由上一阶段的阶段和状态,导出这一阶段的方程。
  • 写出规划方程:就是将各阶段的状态,使用统一的表达式表示。

三个经典实例

币值最大化问题

问题描述:

给定一排n个硬币,面值为正整数C1、C2……Cn(硬币两两可能不相同)。如何选择硬币,使得在其原始位置互不相邻总币值最大。

解题思路:

首先我们假设最优币值为F(N),然后我们试着从这往前推,只能有两种情况:

  • 如果在我的最优选择序列中包含硬币Cn,那么就有F(N)=F(N-2)+Cn(因为要求选择的两个硬币不相邻,所以选择了Cn,就不可能有Cn-1)。
  • 如果在我的最优选择序列中没有Cn,那么就会有F(N)=F(N-1)。因为如果序列的最后一枚硬币没有选的话,那么对于给N和N-1枚的最优决策将会是一样的。

听起来好像有一点贪心的感觉,但是动态规划的过程是更加严谨的。那么我们就可以根据这个逻辑来建立每个状态的递推关系式(亦称状态转移方程):

F(N) = MAX{ F(N-1) , F(N-2)+Cn }

然后我们需要设置初始值(可以类比成递归方法中的终止条件):F(0) = 0, F(1)=C1。然后从F(0)开始逐步计算规模越来越大的子问题的解,并且每次将子问题的解记录在表中,这样方便在需要使用的时候直接调用。逐次迭代,最后就能计算出原问题的解。

伪码描述:

CoinRow ( C( 1……n) ){
        F(0) = 0;
        F(1) = 1;
        for( i = 2; i<=N ; i++ ){
                F(i) = MAX{ F(i-1) , F(i-2) + Ci }
        }
        return F(n);
}

找零问题

问题描述:

现有整数金额N需要找零,零钱币值为D1<D2<D3<……Dn(均为正整数),并且假定每种币值无限多,设计一种最优策略使得找零的硬币数量F(N)最少。

解题思路:

依旧按照最优性原理的方向思考,假设在最优决策下需要选择X枚硬币,并且第X枚硬币的币值为Dx,那么请问这个最优策略的前X-1步,是不是找零金额为N-Dx这个子问题的最优决策。答案是必然的!!!那么由此逻辑,我们可以建立状态转移方程:

F(N) = MIN{ F(N-Di) } + 1      ( i = 1、2……n )

 而这个状态转移方程的初始条件应该是:F(0)=0。

这个式子不知道大家理解起来会不会有点困难,反正我是觉得有点抽象>﹏<这里给出一个过程图方便大家理解:

给定找零金额为N=6,零钱币值为1,3,4,初始条件问F(0)=0。

由递归方程:F(N) = MIN{ F(N-Di) } + 1:

F(1) = MIN{ F(1-Di) } +1,所以F(1)的值为1。

 

以上是一个简单示例的模拟过程,相信大家能够有更好的理解。

 伪码描述:

ChangeMarking ( D[1……m] , N ) {
        F(0) = 0;
        F(1) = 1;
        for( i=2 ; i <= N ; i++ ){
                j = 1 ;
                temp = ∞ ;
                while( i >= D[j] && j<=m ){
                        temp = MIN{ F( i - D[j] ) , temp };
                        j = j + 1;
                }
                F(i) = temp + 1;
        }
        return F(N);
}

收集问题

问题描述:

为了方便描述,这里配上图讲解:

现有机器人从矩阵的左上角出发,要到达右下角,希望你设计一条道路,使得机器人沿路径收集到的金币最多(要求机器人每次只能往右或者往下移动一格)。

解题思路:

直观感受来看,好像是比较简单的,但是计算机没有人脑那么发达,所以想找到一个通用的方法似乎又并不简单。这里我们先总结一下之前两个例子,似乎都是从后往前思考,这种逆向思维往往是的问题变得更简单了。

不可置疑的是,机器人最后一定会走到右下角,而只有两个位置可能通过一次移动到达:也就是最右下角的上边一格或者是左边一格,而这个规律对于矩阵中的每一个位置是有普适性的,因此是可以建立递归方程的。

F( i , j ) = MAX{ F( i-1 , j ) , F( i , j -1 ) } + Cij

这样一个状态转移方程就建立好了(Cij代表此处有没有金币),然后我们要做的就是找到初始条件,那么肯定是猜测初始条件会在左上方的边界处:也就是当递归到的一行或者第一列时就应该停止了,因为矩阵的第一行都没有上面一格,而第一列都没有左边一格。因此我们不妨以此规定初始条件:

F( 0 , j ) = 0 / ( 1 <= i <= n )

且 F( i , 0) = 0 / ( 1 <= j <= n )

过程理解:

首先要根据初始条件确定F( 1 ,1 )的值,(注意蓝色的部分是序列号)。

然后按照伪代码的逻辑是先要处理边界情况,也就是第一行,伪码中用了一个额外的for循环。而第一列中的情况是在后面的双层循环中放在第一个处理。

这里的图没有画好,实际上并不是直接先一次性处理完第一列,而是每次更新 i 的时候先处理每行的第一个。

就这样一行一行的遍历下去:

最后就可以得到如右图所示的一张矩阵,表中每个位置存储的是,从(1,1)到(i ,j)的所有路径中能搜集到的最多硬币数量。然后通过回溯就可以得到最优决策路径。

 伪码描述:

RobotCoinCollection ( D[1……n,1……m] ) {
        F(1,1) = C( i , j );
        for( j = 2 ; j < = m ; i++ )  F( 1, j ) = F( 1, j-1 ) + C( i , j );
        for( i = 2 ; i <= n ; i++ ){
                F(  i , 1 ) = F( i -1 , 1);
                for( j = 2 ; j <= m ; j++ ){
                        F(  i , j ) = MAX { F( i -1 , j ) , F( i , j - 1 ) };
                }
        }
        return F( n , m );
}

背包问题

问题描述:

现在有一个容量为W的背包,有N件物品(物品所占容量分别为:w1,w2……wn/物品的价值分别为:v1,v2……vn),现要使得在不超过背包容量的情况下,所带物品的价值最大。

解题思路:

其实通过前面的三个实例,了解了动态规划的基本模式之后,再来看这道题就不会毫无头绪。

首先一样通过逆向思维来分析一下该问题的各个阶段,为了方便分析,我们不妨假设对于背包容量为W,N件物品的问题通过最有决策后的最大价值为V( W , N )。 可以回想一下币值最大化的解题  逻辑,假设最后一件物品被放进了背包,那么就有V( W , N ) =  V( W , N-1),而如果最后一件物品没有被放进背包,那么就有V( W , N ) = MAX { V( W-wn , N-1) + vn , V( W , N-1) } 。这两个式子也就构成了递推公式。

接着来确定初始值,还是比较好想到的,就是当背包容量为零时,或者物品的价值为零时:

V( i , 0 ) = 0;

V( 0 , j ) = 0;

伪码描述:

for( i = 0 ; i <= W ; i++ ) V( i , 0 ) = 0;
for( j = 1 ; j <= N ; j++ ) V( 0 , j ) = 0;
for( j = 1 ; j <= N ; i++ ){
        for( i = 1 ; i <= W ; j++ ){
                if( i - w[ j ] >= 0){
                        V( i , j ) = MAX { V( i - w[j] , j -1 ) + vj , V( i , j-1 ) = 0 }
                }
                else{
                        V( i , j) = V( i , j -1 );
                }
        }
}
return V( W , N );

以上就是动态规划的一些基本例子,还有一个例子我没有写上来,就是带有记忆功能的动态规划技术解决背包问题。后面有时间我会补上。 

从CCPC河南省赛实战复盘:看大学生如何攻克算法难题与团队协作 本文详细复盘了大学生团队参加CCPC河南省赛的全过程,从赛前准备、比赛策略到实战解题技巧。通过分享团队分工、算法选择、调试经验等关键环节,展现了大学生如何攻克算法难题并提升团队协作能力,为参加程序设计竞赛的选手提供宝贵参考。 阅读详情

相关推荐

程序设计实践考试的入门模板

程序设计实践考试的入门模板 前言 其实从大二开始就在整理有关如何学习C语言以及如何应对程序设计实践(和C语言考试)的经验和相关模板,由于各种原因,这件事情也没有一个很好的进展。前不久邹大佬提起这事儿的时候,突然觉得是应该好好整理一份类似于参考资料的东西了。 我打算先由自己整理出来这份模板,主要面向应对程序设计实践考试的同学。 本文当中可能会存在一些错误和遗漏的东西,还请指正。(email 1278...

mMingfunnyTree的博客 3377

204_蓄水池 动态规划学习例题C解决

题目 2020年c程序设计实践实训题中被考察。 任务描述 工地有一个n升蓄水池,现在需要将它灌满水(不能溢出),当第i次灌水的时候,可以灌入1至num[i-1]升水。 问有多少种灌满水的方法?答案可能很大,答案对1e9+7取模。 • 1 <= n <= 10^6 • 1 <= num[i] <=10^6 思路 题目的意思是,给我们数组的大小和内容,让我们计算达到最大数目的灌水方法有多少种。 例如 给出数字 4 1 3 2 1 意味着最终累加结果为4 可以灌水4次同时

Qiao_er682的博客 1428

山东大学计算机科学与技术学院程序设计思维与实践作业 week11-动态规划(1)

山东大学计算机科学与技术学院程序设计思维与实践作业 山大程序设计思维与实践作业 sdu程序设计思维与实践 山东大学程序设计思维实践作业H11 山大程序设计思维实践作业H11 山东大学程序设计思维与实践 week11-动态规划(1) 动态规划相关资料:GitHub题目描述 楼上有 n 级台阶,其中有 m 级台阶是不安全的。yhf 一开始站在第 0 级台阶上,希望最终走到第 n 级台阶 yhf 跨一步满足以下约束: • 只能向前走 • 不能落脚在不安全的台阶上 • 最多迈 k 级台阶 • 落脚点不能超过第 n

m0_57734028的博客 2185

【LeetCode】买卖股票的最佳时机含冷冻期

【代码】【LeetCode】买卖股票的最佳时机含冷冻期。

wh9109的博客 452

山东大学计算机科学与技术学院程序设计思维与实践作业 week13-动态规划(3)

山东大学计算机科学与技术学院程序设计思维与实践作业 山大程序设计思维与实践作业 sdu程序设计思维与实践 山东大学程序设计思维实践作业H 山大程序设计思维实践作业H 山东大学程序设计思维与实践 week13-动态规划(3)相关资料:GitHub题目描述 将 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。请编写一个程序,读入堆数 N 及每堆的石子数,并进行如下计算:选择一种合并石子的方案,使得做 N−1 次合并得分总和最大。

m0_57734028的博客 1880

程序设计思维与实践 Week11 作业

程序设计思维与实践 Week11 作业 A - 必做题11-1题目分析代码B - 必做题11-2分析代码C - 必做题11-3题目分析D - 必做题11-4题目分析E - 选做题11-1 东东与 ATM题目分析F - 选做题11-2 东东开车了题目题目分析 A - 必做题11-1 题目 题目地址 分析 简单的模拟题,只要在20年内模拟每一年的价格就可以 代码 #include<iostrea...

qq_43688155的博客 1473

ACM中国国家集训队论文集目录(1999-2009)

国家集训队1999论文集 陈宏:《数据结构的选择与算法效率——从IOI98试题PICTURE谈起》 来煜坤:《把握本质,灵活运用——动态规划的深入探讨》 齐鑫:《搜索方法中的剪枝优化》 邵铮:《数学模型的建立、比较和应用》 石润婷:《隐蔽化、多维化、开放化——论当今信息学竞赛中数学建模的灵活性》 杨帆:《准确性、全面性、美观性——测试数据设计中的三要素》 周咏基:《论随机化算法的

ACM,再见! 1923

ACM程序设计竞赛题目解析与实战指南

本文还有配套的精品资源,点击获取 简介:本书收录了ACM国际大学生程序设计竞赛的历年试题与解答,旨在帮助参赛者提升算法设计、问题解决和编程能力。内容包括数据结构、排序算法、搜索算法、动态规划、贪心策略、图论问题等关键知识点,同时涵盖了高级技巧如位运算、字符串处理、数学知识和编码技巧。每道题目都附有详细的问题背景、解题思路和算法实现,帮助读者深刻理解并优化代码,提高逻辑思维能...

weixin_42579969的博客 1809

ACM课程总结

ACM程序设计总结      在大一下学期的时候,老师就开了ACM程序设计这门课,当时也跟着老师上了几节课。一开始还是挺有兴趣的,感觉可以学到很多知识,但是学了几节课之后发现自己有好多知识都不知道,完全跟不上老师的思路,虽然C++在那个时候已经学习的差不多了,但是自己能够真正掌握的还是很少的。而且自己在上大学之前对程序设计语言一点都不了解,又不像男生头脑灵活,所以学习起来很吃力。直到大二又有了选

慧吖的博客 2663

NUIST程序设计基础与算法实践题库

OJ(Online Judge)系统是一种在线程序评测系统,它允许用户提交代码,并通过自动化的脚本运行这些代码来测试它们的正确性、效率和其他指标。一个典型的OJ系统通常包括以下几个关键组成部分:问题(Problem)管理模块:负责管理题目的展示、描述、输入输出格式、样例数据等信息。用户(User)管理模块:处理用户注册、登录、权限、提交记录等功能。提交(Submission)系统:允许用户提交源代码文件,并记录提交时间、提交者信息等。评测(Judging)系统。

weixin_28736145的博客 1182

深入解析与实现:实用算法的程序设计

本文还有配套的精品资源,点击获取 简介:《实用算法的分析与程序设计》这本书专注于计算机科学中的算法理论及其编程实践,目的是让学生掌握并运用常用算法,提高编程效率和可读性。本书涵盖算法分析、常用排序和搜索算法、图算法以及动态规划等高级技术,同时强调了数据结构和多种编程语言在算法实现中的重要性。此外,书中还提供了实际应用案例,如搜索引擎和推荐系统的构建,旨在提升读者的问题解决能...

weixin_36427956的博客 699

ACM:《挑战程序设计竞赛》

前言: 这篇博客是对《挑战程序设计竞赛》的分章节总结。 文章目录第一章:蓄势待发——准备篇第二章:初出茅庐——初级篇 第一章:蓄势待发——准备篇 题目名称 题目标签 题目难度 解题报告 三角形 爆搜 ✦✧✧✧✧ 戳这里 Ants 脑洞 ✦✦✧✧✧ 戳这里 难度增加的抽签问题 二分 ✦✦✧✧✧ 戳这里 第二章:初出茅庐——初级篇 题目名称 题目标签 题目难度 解...

Wangpeiyi9979的博客 2213

ACM国际大学生程序设计竞赛试题精讲与代码

ACM国际大学生程序设计竞赛(International Collegiate Programming Contest,简称ACM-ICPC)是一项面向全球大学生的计算机程序设计竞赛。自1970年首次举办以来,ACM-ICPC已经成为世界上最具影响力的计算机竞赛之一,它不仅考验参赛者的编程能力,更强调算法、数据结构、数学知识和团队协作能力的综合运用。

weixin_36197669的博客 1022

《挑战程序设计竞赛》--初级篇习题POJ部分【动态规划】

关于基本的动态规划和经典的动态规划,在之前已经总结过了,可以温习一下: 传送门 这次是延续上次的《挑战程序设计竞赛》初级篇,总结部分poj上的练习题,主要是DP方面的练习题: 一、基础的动态规划算法 1.Cow Bowing 题目大意:当牛去打保龄球的时候,它们不用真正的保龄球。他们每人取一个数字(范围为0…99),然后排成一个标准的像保龄球针一样的三角形,就像这样: 7 * 3 8 * 8 1 0 * 2 7 4 4

qq_42873760的博客 4001

北大ACM程序设计竞赛真题解析与实战训练

ACM国际大学生程序设计竞赛(ICPC)起源于1970年代,现由ICPC基金会主办,被誉为“计算机界的奥林匹克”。赛事以团队协作、限时解题为核心,每队需在5小时内完成8–13道算法难题,全面考察编程实现、算法设计与心理抗压能力。其赛制严谨、题目创新性强,吸引了哈佛、麻省理工、清华大学、北京大学等世界顶尖高校参与,成为衡量高校计算机人才培养质量的重要标尺。所谓最优子结构(Optimal Substructure),是指一个问题的最优解包含其子问题的最优解。

weixin_42186387的博客 1136

《深入浅出程序设计竞赛》读书笔记

将搜索算法与树结构的关系进行深入剖析,提供了更直观的认识,使我得学习的深度有了质的提升。通过对最短路径算法的分析,可以清晰地看到图的遍历在算法中的地位,这直接关系到了图结构的性质。作者通过实例引导读者思考如何灵活运用数据结构解决问题,以简单而贴切的语言解释了竞赛的本质,同时在讨论算法时,强调了数据结构的重要性,使我对学习数据结构的动力得以提升。一个序列即是X序列的子序列,也是Y序列的子序列,则该序列称为为X和Y的公共子序列。2.分区,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

Qie_Zis的博客 1523

动态规划的深入探讨

一、引言 动态规划是一种重要的程序设计思想,具有广泛的应用价值。使用动态规划思想来设计算法,对于不少问题往往具有高时效,因而,对于能够使用动态规划思想来解决的问题,使用动态规划是比较明智的选择。 能够用动态规划解决的问题,往往是最优化问题,且问题的最优解(或特定解)的局部往往是局部问题在相应条件下的最优解,而且问题的最优解与其子问题的最优解要有一定的关联,要能建立递推关系。如果这种关...

少说,多做。 3357

逆向实战:TikTok X-Gorgon算法解析与Unidg补环境技巧

本文详细解析了TikTok X-Gorgon算法的逆向工程全流程,包括参数定位、变种RC4算法还原及Unidbg补环境技巧。通过动态调试与静态分析相结合,揭示了X-Gorgon的生成逻辑和加密细节,为移动安全研究提供了实用指南。

weixin_28568819的博客 695

2019年安徽省程序设计大赛题解

2019年安徽省程序程序设计大赛题解 A:机器人足球 B:纸牌识别 C:卡牌对决 D:自驾游 E:现代艺术 F:邻家割草 G:括号序列 H:不要回文 I:你的名字 J:密信 K:福报 L:曲奇工厂

hekun博客 2万+

【嵌入式Linux】EC20模块多运营商自适应PPP拨号实战:从IMSI识别到网络配置

本文详细介绍了在嵌入式Linux系统中,如何基于EC20 4G模块实现多运营商自适应PPP拨号。通过解析SIM卡的IMSI识别运营商,并动态配置对应的APN和拨号参数,结合AT指令交互与健壮的pppd脚本,解决了设备跨地域部署时的网络自动连接问题,提升了物联网设备的部署效率和稳定性。

fox11的博客 904

Ubuntu图形界面崩溃?从tty到GUI的终极修复指南

本文提供了Ubuntu图形界面崩溃后进入tty的终极修复指南。详细介绍了如何诊断显示管理器(如LightDM或GDM3)状态,并通过重装、切换显示管理器、修复依赖、系统升级及排查显卡驱动等核心方案,逐步恢复GUI。帮助用户从命令行界面高效修复桌面环境,避免数据丢失。

vulkan6gpu的博客 778
上一篇: 线性表题解
下一篇: 程序设计方法与实践-时空权衡
小陈又菜
博客等级 码龄3年 680粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值