DP专场。。
动态规划是运筹学的一个分支, 求解决策过程最优化的数学方法。
我们一般把动态规划简称为DP(Dynamic Programming)
1.动态规划的背包问题
有一个容量为m的背包,有n个物品,每一个物品i的重量为w[i],价值为v[i]。
要求选择一些物品放入背包中,每种物品只能最多使用一次,使得在不超重的情况下让背包中所有物品价值总和最大。
正常向解法:设状态数组f[i][j]为把前i个物品放入一个容量为j的背包中所能获得的最大价值(以下同设),则状态转移方程为:
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i])
可优化至一维数组,令j从大到小枚举。
f[j] = max(f[j],f[j-w[i]]+v[i])
1.1完全背包
仍然是容量为m的背包,n个物品,每一个物品i的重量为w[i],价值为v[i]。
要求选择一些物品放入背包中,并且每一件物品可以选无限多次,使得在不超重的情况下让背包中所有物品价值总和最大。
f[i][j] = max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i])
第一维也可以优化。
f[j] = max(f[j],f[j-k*w[i]]+k*v[i])
1.2多重背包
依旧是容量为m的背包,n个物品,每一个物品i的重量为w[i],价值为v[i]。
要求选择一些物品放入背包中,并且每一件物品的数量为s[i],使得在不超重的情况下让背包中所有物品价值总和最大。
它相对于完全背包的不同之处是每种物品可选的数量有一个上限。
f[i][j] = max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]),k*v[i] ≤ j, k ≤ s[i]
它的时间复杂度是O(m*Sigma(s[i])),而且看起来并不能像之前那样优化了。。
实际上它是可以优化的。
用「二进制拆分」即可。
对每种物品,我们将它转化成若干个物品,其中每个物品有一个系数,这个物品的费用和价值均是原费用和原价值乘这个系数。令这些系数分别为1,2,4,...,2^k-1,s[i]-(2^k)+1,且k是满足s[i]-(2^k)+1>0的最大整数,例如,如果s[i] = 11,就将这种物品分成系数分别为1,2,4,4这四个物品。
这些物品的系数和能拼出[0,s[i]]中的任意一个整数。这样一来我们就把一个n个物品的多重背包问题转化成了一个Sigma(log s[i])个物品的01背包问题了。
时间复杂度为O(m*Sigma(log s[i]))
实际上,这还能再优化。。。
用单调队列。
单调队列形似一个普通队列,但其中的点按顺序存在某种单调性(我记得我Day1的整理上提到过)。
不同于优先队列,他俩内部结构不太一样。
注意转移式:f[i][j] = max(f[i-1][j],f[i-1][j-k*w[i]]+k*v[i]),这时候k的范围已经变成了k≤min(s[i],ceil(j/w[i])).
对于某一个i而言,因为j = k*w[i]+s的点只能转移到j = (k+1)*w[i]+s , (k+2)*w[i]+s...,所以我们可以按对w[i]取模的值对j进行分类。
把模w[i]相等的点放在一起进行转移,转移后的点会放入单调队列中。我们发现由于队列中的点j是从小到大的,所以会存在转移某一点k,队首点head已经不能作为转移点,即(k-head)/w[i]>s[i]。
显然,该点在以后的转移中也不会成为转移点,所以直接弹出队首即可,直到队首可以作为转移点。转移时选择队首的点作为转移点, 因为这个单调队列是保证转移式是单调递减的。
将当前点k压入队列时,需要判断队尾点tail与k的优劣。如果tail作为转移点不比k更优,显然tail在以后的转移也不会作为转移点了,弹出tail,直至tail比k更优。
对于每个i来说,单次转移是O(m)的,所以时间复杂度是O(nm).
2.动态规划的线性(序列)类(一维)问题
经典例题:一个长度为n的数列,第i个数为a[i],要求选择连续且非空的一段,使这一段中所有数的和加起来最大,输出这个最大和。
正在看这篇随笔的读者朋友,您应该会显然想到O(n^2)的做法。
我们考虑DP。令f[i]表示以i为结尾的最大和子段,那么转移只有:和前面的最大子段和连起来,或者自己单独成为一段。
f[i] = a[i] +max(f[i-1],0)
答案是max(f[i]),i∈[1,n]。
时间复杂度O(n)
2.1 LIS
有一个长度为n的序列,第i个数为a[i],求最长上升子序列的长度。
最长上升子序列的英文名叫Longest Increasing Subsequence,简称LIS。
正在看这篇随笔的读者朋友,您应该会显然想到O(n^2)的做法。
我们重点讨论O(nlogn)的做法。
我们设一个数组g[i],它表示长度为i的LIS中,作为结尾最小的数。
如果原数组a[i] = {6,7,1,5,4,3,4,2,8},那么g数组的变化应该是:
1:g [1] = 6
2:g [1] = 6; g [2] = 7
3:g [1] = 1; g [2] = 7
4:g [1] = 1; g [2] = 5
5:g [1] = 1; g [2] = 4
6:g [1] = 1; g [2] = 3
7:g [1] = 1; g [2] = 3; g [3] = 4
8:g [1] = 1; g [2] = 2; g [3] = 4
9:g [1] = 1; g [2] = 2; g [3] = 3; g [4] = 8
容易看出,每次g数组只会修改一个值或者加入一个值。而且无论如何,g数组总是保持单调递增。
显然,当LIS的长度相等时,结尾的数肯定是越小越好,这样才有更大的机会与后面的数相接,才能生成更长的LIS。这样一来,g数组一定是单调递增的,因为g数组不会存一个较长的LIS,使得它的结尾数比较短的LIS还小。每次考虑以i结尾的上升子序列时,我们只要在g数组中找到最大的的小于a[i]的位置j,令g[j+1] = len即可。
由于g单调,所以可以用二分查找来找到位置j,单次转移复杂度为O(logn)。
最终复杂度为O(nlogn).
例:有两个序列a,b,长度分别为n,m,求它们的LCS。
LCS指最长公共子序列。表示从两个序列中各自选出长度相等的子序列,这两个子序列的数对应相等。
设f[i][j]表示两个序列分别dp到第i,j个数,公共子序列的最大长度。
则有
f[i][j] = max(f[i-1][j],f[i][j-1]) (a[i]!=b[j])
f[i][j] = f[i-1][j-1] + 1(a[i] == b[j])
时间复杂度O(n^2)。
例2:在上例条件不变的情况下,求LCIS
LCIS指最长公共上升子序列。表示从两个序列中各自选出长度相等的上升子序列,这两个子序列的数对应相等。
设f[i][j]表示两序列分别以i,j结尾的LCIS。
一个简单的想法是
f[i][j] = max(f[k][l]+1) (k < i , l < j, a[k] < a[i], b[l] < b[j], a[i]==b[j])
我们先枚举i,再枚举j,当a[i] != b[j] 时f[i][j] = 0。但当a[i] >b[j] 时,在同一个i下,所有f[k][j] (k<i)是可以作为转移点转移后面的状态f[i][j'],因为存在方案的f[k][j]一定保证a[k]<a[i],b[j]<b[j']。
我们可以开一个数组来记录第二维为j且第一维小于i的f的最大值,不过第一维是可以被优化掉的。
设计状态f[i]表示b序列以b[i]结尾的LCIS,具体实现:
1 for (int i=1;i<=n;i++){
2     int k= 0;
3     for (int j=1;j<=m;j++)
4         if (a[i] == b[j])
5             f[j] = max(f[j],k+1);
6         else
7         if (a[i] > b[j])
8             k = max(k,f[j]);
9 }

空间复杂度降为O(m)。
3.树形(树上)DP
顾名思义,是在树上所做的动态规划,其基础是树具有严格的层数关系而不会重复的特性。
一般来说树形DP是处理子树的信息以及其相互关系来进行转移。其状态一般会表示成以i为根的子树的DP值。
3.1邻接表存树
在之前的知识点整理上提前说了,这里只提供代码。
 1 struct Edge_tree{
 2     int u,v,w;
 3     int next;
 4
 5 };
 6 Edge_tree edge[maxn];
 7 int cnt = 0;
 8 int first[maxn];
 9 void add_edge(int from,int to,int dis){
10     edge[++cnt].u = from;
11     edge[cnt].v = to;
12     edge[cnt].w = dis;
13     edge[cnt].next = fisrt[from];
14     first[from] =cnt;
15
16     edge[++cnt].v = from;
17     edge[cnt].u = to;
18     edge[cnt].w = dis;
19     edge[cnt].next = first[to];
20     first[to] = cnt;
21
22 }
23
24
25 void dfs_tree(int x,int fa){
26     //cout << x << " ";
27     for (int i = first[x];i!=0;i = edge[i].next)
28         if (edge[i].v != fa)
29             dfs_tree(edge[i].v,x);
30 }

3.2最大连通子树
有一个n个节点的树,每个点有点权a[i],求一棵连通子树使点权之和最大。
(n ≤ 10^5,|a[i]| ≤ 10^9)
f[u] = a[u] + Sigma(max(f[v],0))
最终答案是max(f[i]), i∈[1,n]
3.3树的直径
有一个n个节点的树,每条边有边权w[i],求一条路径使得它的所有边权和最大。
这条路径就叫做这棵树的直径。
(n ≤ 10^5,|a[i]| ≤ 10^9)
如果边权保证非负,我们可以用两遍bfs求得直径。具体做法是第一次随便选一个点,bfs求得与它距离最远的点x,再从x出发bfs求得与x距离最远的点y,x与y之间的距离就是树的直径。
若边权存在负数,则不能使用这个方法。
设f[u]表示在u为根的子树中,存在点u的最大路径边权和(u可以为端点也可以为中间的点)
设g[u]表示在u为根的子树中,存在点u的最大路径边权和(u只能为端点,即由u发出的一条链)
f[u] = max( firstmax{g[v] + w(u,v)}, 0) + max(secondmax{g[v] + w(u,v)},0)
g[u] = max{g[v] + w (u,v)}
4.区间DP以及其他DP
4.1区间DP
顾名思义,是在一个区间上进行的一系列动态规划,一般考虑对于每段区间,它们的最优值都是由两段或者更多段的小区间点的最优值得到,是分治思想的一种应用。
一般定义状态f[i][j]表示从区间i到j的DP最优值,转移时枚举中间点k,从f[i][k],f[k+1][j]来进行合并
例题:合并石子
n堆石子排成一列,每堆石子有一个重量w[i],每次可以合并相邻的两堆石子,一次合并的代价为二者重量之和。问怎样安排合并顺序使得代价最小。
n,w ≤100
解:用s[i]表示石子的前缀和,有
f[i][j] = min(f[i][k]+f[k+1][j]+s[j]-s[i-1])
时间复杂度O(n^3)。
变式:n堆石子排成一个圆,其他条件不变。
解:在后面加上一条排列相同的石子堆,扩展到2n-1个石子。依然是用s[i]表示w[i]的前缀和。有
f[i][j] = min(f[i][k]+f[k+1][j]+s[j]-s[i-1])
时间复杂度O(n^3),最终答案是min(f[i][i+n-1]),其中1 ≤ i ≤ n。
变式:使最终代价最大, 其他条件不变
解:贪心的来想,我们肯定是让每个石子重量都尽可能多的被计算,也就是说每次只合并一个石子进来应该是一个最优的策略。
f[i][j] = max(f[i+1][j],f[i][j-1])+s[j]-s[i-1]
时间复杂度O(n^2)。
4.2棋盘DP
非常好想的一类DP,在一个二维网格(地图)上做DP。
一般设f[i][j]表示走到(i,j)位置上的最优值。
4.3DAG上的DP
给定一个DAG(有向无环图),要求统计一些信息。
DAG是一个比较规则的结构,我们可以对这些点进行拓扑排序后再DP。
4.4状压DP
(GTMDNOIP2016D2T3
状态压缩DP,通过二进制位上的0/1表示状态,一般用于要记录一段较小规模的状态且该状态包含信息较多的问题。
就目前来说,在NOIP史上只有去年考到了。
谁知道今年还会考什么奇怪的东西呢……
4.5概率期望DP
(GTMDNOIP2016D1T3
这种题目会丧心病狂的让你求某一事件的期望或者概率。。
并不想写这类丧心病狂的东西。
4.6数位DP
(这已经超纲了吧。
这种题目一般会让你统计某一区间内的与数位或与数相关的信息,但由于区间较大而没法暴力求解,所以要在数位上进行DP。
就目前来说,没考过,谁知道今年考不考。。
5.相关优化
5.1前缀和优化
一般用于转移点的取值是一段连续的区间,做一下前缀和可以把转移从O(n)降至O(1)。
5.2滚动数组优化
这个还挺常见的。一般用于二维及以上的DP。如果某一维i的dp值只与i-1的dp值有关那么我们不用存这一维全部的情况,用0/1状态来存储当前状态和转移点状态就可以了,这样会降低空间复杂度。
5.3单调性优化
利用对某一属性的单调性来加速转移,体现在减少转移点数量和快速查询转移点的情况,常见的是单调队列, 二分查找等。
5.4数据结构优化
即利用一些数据结构来进行优化。NOIP阶段常用堆。
5.5其他优化
减少冗余状态
利用数据结构的特殊性质
女装

转载于:https://www.cnblogs.com/OIerShawnZhou/p/7401401.html

夏令营讲课内容整理 Day 5.相关推荐

  1. 人工智能/云原生/数据科学/计算等方向内容整理志愿者招募了!

    持续招募内容整理志愿者!云原生.数据科学.AI.低代码.计算等方向,有意愿的小伙伴,欢迎识别二维码提前报名哦.我们将持续为爱学习.有时间的小伙伴,提供多重福利! 要求: 1. 你需要具备一定学术背景, ...

  2. CSS核心内容整理 - (中)

    2019独角兽企业重金招聘Python工程师标准>>> 本文是CSS核心内容整理的第二篇,承接上一篇的内容继续对CSS的一些重要内容进行整理,推荐先看完这个系列的上一篇. 四.  页 ...

  3. linux过滤输出内容,Linux内容整理--过滤器、输入输出及管道

    Linux内容整理--过滤器.输入输出及管道1.过滤器 Linux中的应用工具分为三种: 1.交互工具 2.过滤器 3.编辑器 能够接受数据,过滤再输出的工具,称为过滤器. 对过滤器和进程,存在着输入 ...

  4. ACL和NAT内容整理

    目录 一.ACL内容整理 1.1ACL的作用与原理 1.2ACL的种类 1.3ACL应用规则 1.4ACL命令 二.NAT内容整理 1.1什么是NAT 1.2NAT的工作原理: 1.3NAT功能: 1 ...

  5. 【项目管理】Scrum内容整理

    针对Scrum相关内容整理如下:(持续更新补充) 目录 定义 角色 四个会议 实施流程 工具 通用实践 敏捷价值观 (更重视左边) 敏捷原则 相关观点 定义 Scrum是迭代式增量软件开发过程,通常用 ...

  6. 【项目管理】RUP内容整理

    针对RUP相关内容整理如下:(持续更新补充) 目录 定义 核心概念 三大特点 核心工作流 十大要素 相关观点 定义 RUP(Rational Unified Process),统一软件开发过程,统一软 ...

  7. 【项目管理】PMO内容整理

    针对PMO相关内容整理如下:(持续更新补充) 目录 定义 职责 引入作用 工作思路 具体落地建议 相关观点 定义 PMO (Project Management Office)一般称为项目管理办公室. ...

  8. 【项目管理】CMMI内容整理

    相关CMMI 内容整理如下:(持续更新补充) 目录 定义 五大级别 引入原因 认证给企业带来的价值 实施流程  相关观点 定义 CMMI的全称为Capability Maturity Model In ...

  9. 云原生/低代码/数据科学/计算等方向内容整理志愿者招募了!

    持续招募内容整理志愿者!云原生.数据科学.AI.低代码.计算等方向,有意愿的小伙伴,欢迎识别二维码提前报名哦.我们将持续为爱学习.有时间的小伙伴,提供多重福利! 要求: 1. 你需要具备一定学术背景, ...

最新文章

  1. python学习音频-机器学习利用Python进行音频数据增强
  2. Python 生成MYSQL inser语句
  3. django oracle clob,记一次clob字段损坏导致的01555错误
  4. java timetasker_Java网络与多线程系列之1:实现一个简单的对象池
  5. Spring 2 和 JPA 简介
  6. python的os,commands,subprocess启动进程调用的几种方法
  7. 线段树(区间合并) LA 3989 Ray, Pass me the dishes!
  8. bmklocationmanager方法没有回调_关于node中的回调(必学)
  9. override java field_JAVA方法的重载(overload)和覆盖(override)
  10. 在家怎么免费下载论文、专利及标准?
  11. 简述窄带调频和宽带调频的_宽带调频和窄带调频的简单区别方法
  12. 基于C51实现测试人体反应速度
  13. 经典算法:遗传算法(GA)
  14. 远端服务器无响应请检查网络,连接远程服务器超时请检查网络连接
  15. 【互联网代理方案】——Zookeeper
  16. 软件工程——四则运算2
  17. 初等数学建模问题-贷款问题(1)
  18. 【MariaDB】安装MariaDB,与MySQL并存
  19. bios和uefi区别
  20. 99mtc:锝99m同位素-小分子/抗体/纳米粒子等材料放射性标记材料标记实验的设计原则

热门文章

  1. 外文翻译 《How we decide》赛场上的四分卫 第三节
  2. oracle你如何重置序列号,oracle sequence语句重置方介绍
  3. VSCode中.py文件找不到路径的解决办法
  4. AIX 操作系统中AIO、DIO、CIO 的相关概念介绍 (二)
  5. C++中相对路径和绝对路径
  6. etcd 日志压缩_etcd集群备份和数据恢复以及优化运维
  7. VIT自适应语音转文本可预测长度和内容(ocr也可)
  8. Object.entries(obj)
  9. 第一个Xcode项目 - 代码修改布局约束
  10. http2-stream-optima-prioritation