动态规划 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,一经查实,立即删除!

相关文章

利用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;安装完成之后可以使用如…

在BurpSuite中安装Jython环境

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

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 …

变量和字符串

变量名就像我们现实社会的名字&#xff0c;把一个值赋值给一个名字时&#xff0c;Ta会存储在内存中&#xff0c;称之为变量&#xff08;variable)&#xff0c;在大多数语言中&#xff0c;都把这种动作称为&#xff08;给变量赋值&#xff09;或&#xff08;把值存储在变量中&am…

Windows安全配置加固

安全配置加固——账号口令 账号优化 目的是为了梳理系统中的账号以及口令&#xff0c;避免默认账号及弱口令的存在 查看账号方式 在Windows中查看账号的方式有以下几种&#xff0c;这里就来简述一下 第一种&#xff1a;开始->运行->compmgmt.msc&#xff08;进入计算…

存根类 测试代码 java_常规单元测试和存根–测​​试技术4

存根类 测试代码 java我的上一个博客是有关测试代码的方法以及讨论您做什么和不必进行测试的方法的一系列博客中的第三篇。 它基于我使用一种非常常见的模式从数据库检索地址的简单方案&#xff1a; …并且我提出了这样的想法&#xff0c;即任何不包含任何逻辑的类实际上都不需…

使用 Python 爬取网页数据

在需要过去一些网页上的信息的时候&#xff0c;使用 Python 写爬虫来爬取十分方便。 1. 使用 urllib.request 获取网页 urllib 是 Python 內建的 HTTP 库, 使用 urllib 可以只需要很简单的步骤就能高效采集数据; 配合 Beautiful 等 HTML 解析库, 可以编写出用于采集网络数据的…

多线程(初级篇)

相关概念进程是指一个内存中运行的应用程序&#xff0c;每个进程都有自己独立的一块内存空间&#xff0c;一个进程中可以启动多个线程。一个进程是一个独立的运行环境&#xff0c;它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了…

了解ADF生命周期中的ADF绑定

在本文中&#xff0c;我将重点介绍ADF绑定层&#xff0c;并探讨当最初从浏览器请求带有某些数据的ADF页面时&#xff0c;它如何工作。 Oracle ADF提供了自己的JSF生命周期扩展版。 实际上&#xff0c;ADF扩展了标准JSF生命周期实现类&#xff0c;并提供了ADF阶段侦听器&#x…

绕过 WAF:绕过一些 WAF 设备的 Burp 插件

我写了这个插件使用的技术博客文章在这里一会儿回来。如果存在特定标头&#xff0c;许多 WAF 设备可能会被诱骗相信请求来自自身&#xff0c;因此是可信的。绕过方法的基础知识可以在此处的 HP 博客文章中找到。 一段时间以来&#xff0c;我一直在 Burp 中实施匹配/替换规则&…

windows apache部署php,Windows下部署Apache+PHP+MySQL运行环境实战

首先是MySQL&#xff0c;(这边吐槽一下被Oracle收购的MySQL)一步一步往下&#xff0c;无需更多的配置然后安装Apache&#xff0c;也是一步一步往下安装PHP&#xff0c;(我偷懒我自豪, 在PHP下载页面找那个Installer的文件.......吐槽可能不是最新的版本 &#xff5e;)下载页面安…

粉丝提问:有没有人会做彩虹表

彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合&#xff0c;不一定是针对MD5算法的&#xff0c;各种算法的都有&#xff0c;有了它可以快速的破解各类密码。越是复杂的密码&#xff0c;需要的彩虹表就越大&#xff0c;现在主流的彩虹表都是100G以上。 …

fofa自动化爬虫脚本更新+详解

fofa自动化爬虫脚本更新详解 起因 最近要用到fofa爬虫&#xff0c;为什么要用爬虫不用api&#xff0c;问就是穷&#xff0c;想起来之前写过一个相关的脚本&#xff1a;Fofa-python-脚本&#xff0c;是很久以前写的了&#xff0c;之前写的时候有点问题&#xff0c;昨天重新修…

【APICloud系列|16】苹果开发者账号如何更改双重认证的手机号

按照一般的更改流程&#xff1a; 现在苹果账号安全系统升级&#xff0c;一般需要同意协议或者和本公司密切相关的人员才能操作。我这种借苹果手机操作的人除外。 那我用win7电脑如何操作呢&#xff1f; 登录苹果开发者账号&#xff0c; 进入如下管理账号地址&#xff1a;htt…

JS文件信息收集工具-LinkFinder

0x00 前言 我们在渗透测试的之前&#xff0c;信息收集是必要的步骤&#xff0c;而JS文件中往往会暴露出很多的信息&#xff0c;比如一些注释的中铭感信息&#xff0c;内网ip地址泄露等等&#xff0c;还会有一些绝对路径或者相对路径的url&#xff0c;而这些url中很有可能就存在…

extjs中Store和grid的刷新问题

问题1&#xff1a;Store.load() 和Store.setproxy()区别 问题2:修改后的Grid 更新&#xff1a; Store.reload() 问题3&#xff0c;store删除后刷新会出问题 Store移除一行&#xff1a;Store.removeAt(Number index) 从数据集中删除指定索引位置的记录     或者Store.reload…

【APICloud系列|28】苹果开发者账号应该如何续费?

本次更新时间:2020/7/13 登录苹果开发者账号,一般还有1个月到期官方会给你发邮件,不懂英文的可以使用谷歌翻译功能。 目前的后台提醒是这样的,我给你翻译一下 这个如果没有到期,使用Apple Developer这个应用程序进行充值缴费。 对应地址:https://developer.apple.com/i…