动态规划

【学习笔记】dp入门

♀尐吖头ヾ 提交于 2019-12-20 00:32:43
知识点 动态规划(简称dp),可以说是各种程序设计中遇到的第一个坎吧,这篇博文是我对dp的一点点理解,希望可以帮助更多人dp入门。 先看看这段话 动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。1957年出版了他的名著《Dynamic Programming》,这是该领域的第一本著作。 这是百度百科上对于动态规划介绍,其实dp并没有这么复杂,可以把dp看作是记忆化搜索的递推形式。 再来看一下下面这段文字(来自 《挑战程序设计竞赛(第二版)》 ): 就像上面所说的,对于一道题目,如果选择搜索可能会出现大量重复的状态,如函数f int f(int a,int b) { return f(a+1,b+1)+f(a-1,b-1); } 这种函数在计算过程中有大量的资源被浪费,如 f(1,1) 和 f(3,3) 在返回值时都会计算一次 f(2,2)

经典动态规划——背包问题系列一

谁说胖子不能爱 提交于 2019-12-19 15:36:07
经典动态规划——背包问题系列一 复赛前发一波博客,虽然意义不是很大了…… 本篇讲的是背包问题基础 01背包问题 简述 有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 思路 动态规划的基本题,背包问题之母。 对动态规划有一定了解的人应该都应理解它的原理和方程。 所谓动态规划,就是把问题分成互相联系的多个阶段决策,每一步决策都能影响答案。当然,各个阶段决策的选取不是任意确定的,它依赖于当前面临的状态,又影响以后的发展。那么我们需要确定,各阶段之间的关系是什么以及各状态如何决策才能使答案最优。 在背包问题中,我们通常把背包容量设为状态。那么我们需要决策,到底如何放置物品,才能使各阶段背包在有限容量内装价值最多的物品? 首先我们需要知道:背包容量大的状态一定由容量小的状态转移过来,因为我们要不断选物品,这样是总重量越来越大。现在我们用f[i][j]示前i件物品恰放入一个容量为v的背包可以获得的最大价值。 我们只要枚举物品,在有限的空间里取物品,并一直取max,就能在规定背包空间内跑完。 \[ dp[i][v] = max(dp[i-1][v],dp[i-1][v-c[i]]+w[i]) \] 为什么要逆着枚举?因为容量大的状态要从容量小的状态转移过来,而我们要固定每个容量去装物品

贪心策略

回眸只為那壹抹淺笑 提交于 2019-12-19 12:38:58
之所以称之为“策略”,而不是“算法”,还是有些原因的,贪心思想和动态规划思想练习紧密,可是细想一下有不能混为一谈,动态规划是根据迁移过程的状态去推导下一过程的状态,是有理论依据的,通过每次“完美”的检验而得出最优解,关键是找出一个最优子结构,记得经典的一句话是:动态规划的子结构必须是独立的,而且是重叠的,虽然是一组反义词,可是代表的意义是不同的。扯远了,而贪心策略不同,贪心只考虑当前的最优解,是临时的,不连续的,所以得出的解不一定是最优解。 一个最间的的例子就是0-1背包问题,根据贪心,每次都放进去最“实惠”的,也就是“性价比”最高的,然后放次高的,直到不能放,这样做显然是不尽人意的,因为贪心策略没有考虑到背包的容量,为什么?前面说过了,贪心策略只考虑当前的最优解,他是不会管背包容量的,如果加入了背包容量这个参数,便成为DP问题了,贪心策略缺少一约束条件,贪心找出的每一条路径都是最大可能接近最优解的,例如:每条路径是最优解的可能性是0.8,贪心一共做了4次检查,于是正确的最优解得可能是(0.8)^4,可是他没有考虑其他的情况,问题就出现了。当然,贪心也有很多正确的策略,只是不适用某一问题罢了。 *********************************************************************** 下面开始看看贪心算法: 一.贪心算法的基本概念

动态规划法

天涯浪子 提交于 2019-12-19 04:30:13
动态规划问题的基本思想 : (1)动态规划问题的关键在于正确地 写出基本的递推关系式和恰当的边界条件(也就是基本方程) 。 要做到这一点,必须将问题的过程划分成几个相互联系的阶段,选取恰当的状态变量,决策变量以及定义最优值函数,从而把一个大问题化成一族同类型的子问题 , 然后逐个求解。即从边界条件开始 , 逐段递推寻优 , 在每一个子问题的求解中 , 均利用了它前面的子问题的最优化结果 , 依次进行 , 最后一个子问题所得的最优解 , 就是整个问题的最优解。 (2)在多阶段决策的过程中,动态规划方法是既把当前一段和未来各段分开,又把当前效益和未来效益结合起来考虑的一种最优化方法。因此,每段决策的选取是 从全局来考虑的。 与该段的最优选择答案一般不同。 (3) 在求整个问题的最优策略时,由于初始状态时已知的,而每段的策略都是该段状态的函数,故最优策略所经过的各段状态便可以逐次变换得到,从而确定了最优路线。 上面的解法称为 逆序解法 ,如果我们把G点作为起点,A点作为终点,则得到了 顺序解法 (思想一样,不做赘述)。但是动态规划的求解方向都与行进方向是相反的。 可以用动态规划法解题的特征: 动态规划求解问题的四个特征: ①求一个问题的最优解; ②整体的问题的最优解是依赖于各个子问题的最优解; ③小问题之间还有相互重叠的更小的子问题; ④从上往下分析问题,从下往上求解问题;

五大常用算法

天大地大妈咪最大 提交于 2019-12-18 18:49:37
https://blog.csdn.net/beyond59241/article/details/50728614 https://blog.csdn.net/changyuanchn/article/details/51476281 据说有人归纳了计算机的五大常用算法,它们是贪婪算法,动态规划算法,分治算法,回溯算法以及分支限界算法。虽然不知道为何要将这五个算法归为最常用的算法,但是毫无疑问,这五个算法是有很多应用场景的,最优化问题大多可以利用这些算法解决。算法的本质就是解决问题。当数据量比较小时,其实根本就不需要什么算法,写一些for循环完全就可以很快速的搞定了,但是当数据量比较大,场景比较复杂的时候,编写for循环就是一个很不明智的方式了。一是耗时,二是写出的代码绝对是天书。当然还有第三点,这点也是最重要的,写代码是一种艺术,而不是搬砖。前面的文章里对这五种算法都已经做了详细的讲解和归纳,本文主要是一个总结,将这五种算法整理到一起来对比,分析一下。 0) 穷举法 穷举法简单粗暴,没有什么问题是搞不定的,只要你肯花时间。同时对于小数据量,穷举法就是最优秀的算法。就像太祖长拳,简单,人人都能会,能解决问题,但是与真正的高手过招,就颓了。 1) 贪婪算法 贪婪算法可以获取到问题的局部最优解,不一定能获取到全局最优解,同时获取最优解的好坏要看贪婪策略的选择。特点就是简单

【算法】算法思想之动态规划

南笙酒味 提交于 2019-12-18 10:08:56
   动态规划 (dynamic Programming)主要解决的问题: 多阶段决策过程最优化, 其主要的思想是将最优化决策过程分为若干个互相联系的阶段, 每个阶段需要作出一个决策 ,并且当前阶段的决策会影响下一阶段的决策,从而影响到整个过程的活动路线。 基本概念 阶段(Stage): 把所给问题的求解,恰当地划分成若干个相互联系的阶段,以便于求解。 通常用k表示阶段变量 。 状态(State): 状态表示 某段的出发位置 。它既是该段某支路的始点,同时也是前一段某支路的终点。通常一个阶段包含若干个状态。描述过程状态的变量,称为 状态变量 。常用Xk表示在第k段的某一状态。 决策(Decision): 决策就是某阶段状态给定以后,从该状态演变到下一阶段某状态的选择,描述决策的变量,称为 决策变量 。常用uk(xk)表示第k段当状态处于xk 时的决策变量。决策变量的取值往往限制在某一范围之内,此范围称为 允许决策集合 。 策略(Policy): 各个阶段所确定的决策就构成一个决策序列,通常称为一个 策略 。 指标函数: 在多阶段决策过程最优化问题中,指标函数是用来衡量所实现过程的优劣的一种数量指标,它是一个定义在全过程和所有 后部子过程 上的确定数量函数,常用Vk,n表示。不同的问题中,指标的含义也不同:距离、利润、成本、产量、资源消耗等。 最优指标函数: 指标函数Vk,n的最优值

动态规划最大子数组和到最大子矩阵

筅森魡賤 提交于 2019-12-17 11:48:51
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 定义 大问题可以分成若干个独立的小问题叫做动态规划。 大问题可以分成若干个需要重复计算的问题叫做分治。 拓展 动态规划可以是单向,可以是横纵结合。 单向:最大子数组问题 结合:最大子矩阵问题 最大子数组问题 假设 m-n 的和最大,则有 max = rangesum(m,n) ,且有, rangesum(0-m)<0 , range(n,len)<0 现实生活中,就是买股票,在最低价的时候买入,在最高价格的时候抛出,形成利益最大化。 int MaxSum(int n,int *a) { int sum=a[0],presum=0; for(int i=0; i<n; i++) { if(presum>0) { presum+=a[i]; //对于前面的和大于0,说明是仍处于盈利状态 } else { presum=a[i]; // 如果小于0,说明出现了亏损。那么这个时候及时止损。 } if(presum>sum) { sum = presum; //0-i出现的最大sum,也就是出现的总体增。 } } return sum; } sum 记录最优解 presum 继续做试探求解。 最大子矩阵 #include<stdio.h> #define N 102 int map[N][N]={0}; int

【算法】动态规划:适合新手的动态规划入门常见题目详解

北慕城南 提交于 2019-12-17 02:51:09
动态规划 一、什么是动态规划 1.官方定义 2.动态规划的特点 二、题目练练手 1.斐波那契数列 2.爬楼梯 3.最大连续子数组和 4. 打家劫舍 一、什么是动态规划 1.官方定义 动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。 2.动态规划的特点 看官方的解释大家应该都会比较懵,通俗的来讲,动态规划就是大事化小,小事化无。要解决一个大问题就将大问题拆解成小问题,通过逐个解决这些小问题,最终解决问题。 动态规划可以说是递归方法的一种优化,在递归问题中,我们需要进行大量的重复计算过程,时间复杂度极高。于是就有了将计算过的结果保存起来,每一个子问题只需要计算一次就好。动态规划实际也是使用 空间换时间 的一种做法。于是动态规划问题就具有这样的特点: 问题可以逐步拆解 所有子问题只需解决一次 对子问题进行存储 在解决动态规划问题时,通常就是以下三个步骤: 状态定义 初始化 状态转移方程

切木棍(区间模型动态规划)

╄→尐↘猪︶ㄣ 提交于 2019-12-16 10:20:05
题目链接 https://vjudge.net/problem/UVA-10003 d[i][j]表示在i到j这一段上切一刀的最小权和,如果没有切过就为0。 状态转移方程为ans(i,j)=min{ans(i,k)+ans(k)(j) | i<k<j } +d[j]-d[i] #include<bits/stdc++.h> #include<algorithm> #define max 0x3f3f3f3f using namespace std; int d[55]; int ans[55][55]; int main(){ int distance; int length,j; while(scanf("%d",&distance)){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&d[i]); d[0]=0,d[n+1]=distance; for(length=1;length-1<=n+1;length++) for( int i=0;i+length-1<=n+1;i++){ j=i+length-1; ans[i][j]=0; int minv=max; for(int k=i+1;k<j;k++){ int temp=ans[i][k]+ans[k][j]+d[j]-d[i]; if(minv

密码脱落(动态规划 LCS)

流过昼夜 提交于 2019-12-16 08:36:02
题目: X星球的考古学家发现了一批古代留下来的密码。 这些密码是由A、B、C、D 四种植物的种子串成的序列。 仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。 由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。 你的任务是: 给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。 输入一行,表示现在看到的密码串(长度不大于1000) 要求输出一个正整数,表示至少脱落了多少个种子。 例如,输入: ABCBA 则程序应该输出: 0 再例如,输入: ABECDCBABC 则程序应该输出: 3 资源约定: 峰值内存消耗 < 256M CPU消耗 < 1000ms 思路: 答案为: 密码串的长度 - 密码串与反密码串的最长公共子序列的长度 代码: import java . util . Scanner ; public class Main { public static void main ( String [ ] args ) { long start = System . currentTimeMillis ( ) ; Scanner sc = new Scanner ( System . in ) ; String in = sc . next ( ) ; String out = "" ; for (