动态规划 dynamic programming

动态规划dynamic programming

June,7, 2015

作者:swanGooseMan

出处:http://www.cnblogs.com/swanGooseMan/p/4556588.html

声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处。

 

1. 什么是动态规划?

  • dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems.引自维基百科
  • 动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
  • 如何拆分问题,是动态规划的核心。而拆分问题,靠的就是状态和状态转移方程的定义。
  • 动态规划的本质(两个重要的概念):状态(形如dp[i][j])、状态转移方程(形如dp[i][j] = dp[i - 1][j] + dp[i -1][j – 1]

动态规划相关的其他几个名词:

a. “缓存”,“重叠子问题”,“记忆化”:
这三个名词,都是在阐述递推式求解的技巧。以Fibonacci数列为例,计算第100项的时候,需要计算第99项和98项;在计算第101项的时候,需要第100项和第99项,这时候你还需要重新计算第99项吗?不需要,你只需要在第一次计算的时候把它记下来就可以了。
上述的需要再次计算的“第99项”,就叫“重叠子问题”。如果没有计算过,就按照递推式计算,如果计算过,直接使用,就像“缓存”一样,这种方法,叫做“记忆化”,这是递推式求解的技巧。这种技巧,通俗的说叫“花费空间来节省时间”。都不是动态规划的本质,不是动态规划的核心。

b. "无后效性",“最优子结构”:
上述的状态转移方程中,等式右边不会用到下标大于左边i或者j的值,这是"无后效性"的通俗上的数学定义,符合这种定义的状态定义,我们可以说它具有“最优子结构”的性质,在动态规划中我们要做的,就是找到这种“最优子结构”。

c. “递归”:
递归是递推式求解的方法。

 

2. 怎么用动态规划?

  • 通常用来求解最优化问题(optimization problem): 这类问题可以有很多可行的解,我们需要找出其中的最优解。应用于子问题重叠的情况。
  • 动态规划通过拆分问题,对每个子问题只求解一次,将其解保存在一个表格(数组)中,从而无需每次求解一个子子问题时都重新计算,当前子问题的解将由上一次子问题的解推出,只需要多项式时间复杂度,因此它比回溯法、暴力法等要快许多。

通常按如下四个步骤来设计动态规划算法:

  1. 刻画一个最优解的结构特征

  2. 递归地定义最优解的值

  3. 计算最优解的值,通常采用自底向上(如Fibonacci1开始)的方法。也可以自顶向下(如Fibonaccin开始)进行求解,但此时需要对解需要进行记录。//3步构成动态规划解的基础。

  4. 利用计算出的最优解的信息构造一个最优解。//此步如果只要求计算最优解的值时,可省略。

即对于具有最优子结构、重叠子问题的最优化问题,通过拆分问题,找出满足无后效性的状态和状态转移方程。(这也是DP的难点所在)

 

3. 实例

DP11447 采药

问题描述:

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 如果你是辰辰,你能完成这个任务吗?

分析:

将问题分解成若干子问题,让问题规模变小

对于最终最优结果(达到最大价值),假如第N个药品没有采,那么最优结果就是总时间为totalTim内采n-1个物品的最大价值。

对于n-1如果不是最大价值(即有比它更大的),那么与最优结果的假设(第N个药品没有采)矛盾,所以满足最优子结构性质,可以使用动态规划算法。

拆分问题

totalTime70)时间内采摘totalDrug3)药草,最终达到最大价值,根据第N3)个物品是否采摘,可分为两种情况:

子问题1:第N个物品没有采摘。即在totalTime70)时间内继续采摘totalDrug-12)药草。

子问题2:第N个物品有采摘。即在totalTime – timen)(69)时间内继续采摘totalDrug-12)药草,此时最优价值应加上该物品的价值vlann)(2)。

状态 time时间内采摘drug颗药草的最大价值dp[drug][time]

状态转移方程dp[drug][time] = max { dp[drug-1][time] , dp[drug-1][time - time(n)]+vlan(n) },dp[n][m] = 较大值{dp[n-1][m], dp[n-1][m-time[n]]+value[n] }

AC源码

#include <iostream>
#include <cstdio>
using namespace std;//物品数组,结构体,时间,价值
typedef struct {int time;int value;
}Drug;
Drug drug[101];int main() {int totalTime, totalDrug;//总采药时间,总药品数int dp[101][1001]={0};//记录表格  dp[总药品数][总采药时间]//读入数据// freopen("input.txt", "r", stdin);scanf("%d%d", &totalTime, &totalDrug);for (int i = 1; i <= totalDrug; i++) {scanf("%d%d", &drug[i].time, &drug[i].value);}//DPfor (int i = 1; i <= totalDrug; i++) {for (int j = 1; j <= totalTime; j++) {  //取两个子问题的最大值dp[i][j] = dp[i-1][j];  //子问题1: 第N个物品没有采摘if (drug[i].time <= j && dp[i][j] < dp[i-1][j-drug[i].time] + drug[i].value) {  //子问题2: 第N个物品有采摘dp[i][j] = dp[i-1][j-drug[i].time] + drug[i].value;}}}printf("%d\n", dp[totalDrug][totalTime]);return 0;
}
View Code

 

DP2 1448最长上升子序列(Longest Increasing Subsequence)

问题描述:

一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

拆分问题:

原问题含n项序列的LIS长度,等价于以第1,2,3,...,n项结尾的LIS长度集合中的最大值,由此拆分为n个子问题,最后求出nLCS的最大者:

n个子问题:以第n项结尾的LIS的长度是:保证第i项比第n项小的情况下,以第i项结尾的LIS长度加一的最大值,取遍i的所有值(i小于n)。

max{dp[i]+1},其中 1<=i<=n-1 array[i] < array[n]

状态:数列中以第n项结尾的最长上升子序列的长度 dp[n]

状态转移方程

dp[1] = 1;(根据状态定义导出边界情况)

dp[n] = max{dp[i]+1},其中 1<=i<=n-1 array[i] < array[n]

AC源码:

#include <iostream>
#include <cstdio>
using namespace std;int lcs(int array[],int n) {int dp[n];int max = 1;  //整个序列的最长递增子序列长度,至少为1for (int i = 0; i < n; ++i) {  //遍历整个序列,分别求出n个子问题的解dp[i]dp[i] = 1;  //以第i项结尾的LIS长度,至少为1,下面进行计算//dp[i]:保证第i项比第n项小的情况下,以第i项结尾的LIS长度加一的最大值.for (int j = 0; j < i; ++j) {  //遍历前0 ~ i-1 项if(array[j] < array[i] && dp[i] < dp[j] + 1)dp[i] = dp[j] + 1;}if(max < dp[i]) max =dp[i];}return max;
}int main(int argc, char const *argv[]) {
// #ifndef _OJ_  //ONLINE_JUDGE// freopen("input.txt", "r", stdin);
//   freopen("output.txt","w",stdout);
// #endifint n;int array[1001];scanf("%d", &n);for (int i = 0; i < n; ++i)scanf("%d", &array[i]);printf("%d\n", lcs(array, n));return 0;
}
View Code


DP41450 最长公共子序列(Longest Common Subsequence

问题描述:

需要你做的就是写一个程序,得出最长公共子序列。
最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCSLongest Common Subsequence)。其定义是,一个序列S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则S 称为已知序列的最长公共子序列。

问题拆分

最长公共子序列的结构有如下表示:

设序列X=<x1, x2, …, xm>Y=<y1, y2, …, yn>的一个最长公共子序列Z=<z1, z2, …, zk>,则可分为以下三种情况:

  1. xm=yn,则zk=xm=ynZk-1Xm-1Yn-1的最长公共子序列;

  2. xm≠ynzk≠xm ,则ZXm-1Y的最长公共子序列;

  3. xm≠ynzk≠yn,则ZXYn-1的最长公共子序列。

其中Xm-1=<x1, x2, …, xm-1>Yn-1=<y1, y2, …, yn-1>Zk-1=<z1, z2, …, zk-1>

状态

dp[i,j]记录序列XiYj的最长公共子序列的长度。其中Xi=<x1, x2, …, xi>Yj=<y1, y2, …, yj>

状态转移方程

i=0j=0时,空序列是XiYj的最长公共子序列,故dp[i,j]=0。其他情况下,由定理可建立递归关系如下:

    | 0              if i=0 or j=0

dp[i][j] = | dp[i-1][j-1]          if i>0 , j>0 and Xi == Yj

    | max{dp[i][j-1], dp[i-1][j]}    if i>0 , j>0 and Xi != Yj

 AC源码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;int lcs(char s1[], char s2[]) {int maxlen = 0;int len1 = strlen(s1), len2 = strlen(s2);int dp[len1 + 1][len2 + 1];  //状态: dp[i,j]记录序列 Xi 和 Yj 的最长公共子序列的长度for (int i = 0; i < len1 + 1; ++i) dp[i][0] = 0;  //根据状态定义导出边界情况 (任一序列与空序列的lcs为0)for (int i = 0; i < len2 + 1; ++i) dp[0][i] = 0;for (int i = 1; i < len1 + 1; ++i) {  //算法核心, 根据状态转移方程, 自底向上计算.for (int j = 1; j < len2 + 1; ++j) {if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);maxlen = max(maxlen,dp[i][j]);}}return maxlen;
}int main(int argc, char const *argv[]) {
// #ifndef _OJ_  //ONLINE_JUDGE// freopen("input.txt", "r", stdin);
//   // freopen("output.txt", "w", stdout);
// #endifint n;char s1[1010], s2[1010];scanf("%d", &n);while (n--) {scanf("%s%s", s1, s2);// gets(s1); gets(s2);printf("%d\n", lcs(s1,s2));}return 0;
}
View Code

 





 





转载于:https://www.cnblogs.com/swanGooseMan/p/4556588.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/358715.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Bash脚本教程之脚本入门

目录 Shebang 行 执行权限和路径 env 命令 注释 脚本参数 shift 命令 getopts 命令 配置项参数终止符 --

php m pi 2,PHP学习(2)

PHP 的自定义常量自定义常量需要我们使用define()函数去定义&#xff0c;用法为define(name,value,case_insensitive)其中case_insensitive为可选参数&#xff0c;规定常量是否区分大小写&#xff0c;值为true(不敏感)或者false(默认&#xff0c;敏感)例子&#xff1a;define(&…

Bash脚本教程之read命令

目录 用法 参数 IFS 变量 用法 有时,脚本需要在执行过程中,由用户提供一部分数据,这时可以使用read命令。它将用户的输入存入一个变量,方便后面的代码使用。用户按下回车键,就表示输入结束。 read命令的格式如下。 read [-options] [variable...] 上面语法中,optio…

利用Vulnhub复现漏洞 - JBoss JMXInvokerServlet 反序列化漏洞

JBoss JMXInvokerServlet 反序列化漏洞 Vulnhub官方复现教程漏洞原理 复现过程启动环境端口设置浏览器设置BurpSuit设置 复现漏洞序列化数据生成发送POCEXP Vulnhub官方复现教程 https://vulhub.org/#/environments/jboss/JMXInvokerServlet-deserialization/ 漏洞原理 这…

linux mysql 安装启动失败,Linux服务器一键安装包的mysql启动失败

Linux服务器上用一键安装包配置的环境&#xff0c;启动mysql失败&#xff0c;提示如下错误信息&#xff1a;排查方法&#xff1a;1、查看服务器的磁盘空间是否正常&#xff0c;登录服务器执行命令df -h查看磁盘空间&#xff0c;如果服务器的系统盘或者数据盘空间满了&#xff0…

Ubuntu 安装mysql和简单操作

ubuntu上安装mysql非常简单只需要几条命令就可以完成。 1. sudo apt-get install mysql-server2. apt-get isntall mysql-client3. sudo apt-get install libmysqlclient-dev安装过程中会提示设置密码什么的&#xff0c;注意设置了不要忘了&#xff0c;安装完成之后可以使用如…

卖家工具箱源码_我的测试和代码分析工具箱

卖家工具箱源码上周&#xff0c;我们在LINEAS成立了一个“测试技能小组”&#xff0c;该小组用于交换有关测试的知识。 各种各样的问题反复出现的一个问题是&#xff1a;有哪些工具可以测试和分析代码&#xff1f; 因此&#xff0c;这是我对此的个人回答&#xff0c;按照我倾向…

Bash脚本教程之条件判断

目录 if 结构 test 命令 判断表达式 文件判断 字符串判断 整数判断 正则判断 test 判断的逻辑运算

matlab工序能力分析,《MATLAB编程与系统仿真》课程考核说明

《MATLAB编程与系统仿真》课程考核说明1、考核方式及考核时间综合性仿真及报告书(60%)实验成绩(30%)平时成绩(10%)&#xff0c;其中实验成绩包括实验和报告。《MATLAB编程与系统仿真》课程是一门实践性比较强的课程&#xff0c;采用传统的试卷考核方式无法体现学生对MATLAB的掌…

在BurpSuite中安装Jython环境

在BurpSuite中安装Jython环境 下载模块 下载地址 https://www.jython.org/download.html 下载 Jython Standalone版本的 打开burp 第一个框子是刚刚下载jar包 第二个时候python的模块文件地址 要到 lib\site-packages里面 成功 转载于&#xff1a;https://blog.csdn.net/w…

在Spring Framework中通过JNDI进行配置

从某个时候开始&#xff0c;应用程序必须是可配置的。 自第一个版本0.9起&#xff0c;Spring Framework就为该问题提供了一个很好的辅助工具&#xff0c;该类为PropertyPlaceholderConfigurer类&#xff0c;而从Spring Framework 3.1起为PropertySourcesPlaceholderConfigurer类…

Bash脚本教程之循环

目录 while 循环 until 循环 for...in 循环 for 循环 break,continue select 结构 Bash 提供三种循环语法for、while和until。 while 循环 while循环有一个判断条件,只要符合条件,就不断循环执行指定的语句。 while condition; docommands done上面代码中,只要满足…

hdu5247 找连续数

Problem Description小度熊拿到了一个无序的数组&#xff0c;对于这个数组&#xff0c;小度熊想知道是否能找到一个k 的区间&#xff0c;里面的 k 个数字排完序后是连续的。现在小度熊增加题目难度&#xff0c;他不想知道是否有这样的 k 的区间&#xff0c;而是想知道有几个这样…

matlab dtft的函数,DTFT的Matlab矩阵计算的理解

其实是早应该想到的&#xff0c;今天写程序的时候想到了。然后&#xff0c;跟同学说起来&#xff0c;说&#xff0c;原来国外的教材很多都是矩阵的形式来表示离散傅里叶变换的。但是国内的教材没有这么表达。一个是&#xff0c;自己看的东西还是少了&#xff0c;一个是&#xf…

xss测试工具(xsstrike基于python)

xsstrike很强 项目地址&#xff1a; https://github.com/s0md3v/XSStrike安装&#xff1a; git clone https://github.com/s0md3v/XSStrike.git使用文档&#xff1a; https://github.com/s0md3v/XSStrike/wiki/Usageusage: xsstrike.py [-h] [-u TARGET] [--data DATA] [-t …

Bash脚本教程之函数

目录 简介 参数变量 return 命令 全局变量和局部变量,local 命令 简介 函数(function)是可以重复使用的代码片段,有利于代码的复用。它与别名(alias)的区别是,别名只适合封装简单的单个命令,函数则可以封装复杂的多行命令。 函数总是在当前 Shell 执行,这是跟脚本…

dcc garch matlab,如何用Eviews或者MATLAB实现DCC-garch模型?

可以在软件中查到说明文件&#xff1a;以下为说明文件的内容In the first box, you should either enter the name of your group or specify the returns as separate series (transforming expressions like dlog() are also allowed). If you wish to use exogenous variabl…

二维GROUP BY

上午参加了个计算机英语三级考试&#xff0c;回来后BA同事让帮忙统计数据。可能刚考完试思维比较混乱&#xff0c; 整理了好大一会没有想明白怎么写。 最后挣扎了快一个小时终于想起来&#xff0c;记下来留个备份&#xff1b; select store_no, brand_code, count(distinct t.d…

Spring Integration Jdbc RowMapper示例

JDBC入站通道适配器的基本功能是执行SQL查询&#xff0c;提取数据并将以Message形式封装的结果集传递到本地通道。 您可以在JDBC入站通道适配器的示例中阅读有关此内容的更多信息。 有效负载的类型由行映射策略决定。 默认情况下会产生类型为List的有效负载&#xff0c;其中每…

Bash脚本教程之数组

目录 创建数组 读取数组 读取单个元素 读取所有成员 默认位置 数组的长度 提取数组序号 提取数组成员