集训Day 6 2020.3.6 动态规划(一)

集训Day 6 2020.3.6

动态规划(一)入门

https://www.luogu.com.cn/training/2774

作业讲解

P2827 蚯蚓

题中,我们将用符号 \(\lfloor c \rfloor\) 表示对\(c\)向下取整,例如:\(\lfloor 3.0 \rfloor = \lfloor 3.1 \rfloor = \lfloor 3.9 \rfloor = 3\)
蛐蛐国里现在共有 \(n\)只蚯蚓(\(n\)为正整数)。每只蚯蚓拥有长度,我们设第 \(i\) 只蚯蚓的长度为 \(a_i (i=1,2,\dots,n)\),并保证所有的长度都是非负整数(即:可能存在长度为\(0\)的蚯蚓)。
每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半。神刀手切开蚯蚓的位置由常数\(p\)(是满足 \(0 < p < 1\) 的有理数)决定,设这只蚯蚓长度为\(x\),神刀手会将其切成两只长度分别为 \(\lfloor px \rfloor\)\(x - \lfloor px \rfloor\) 的蚯蚓。特殊地,如果这两个数的其中一个等于0,则这个长度0的蚯蚓也会被保留。此外,除了刚刚产生的两只新蚯蚓,其余蚯蚓的长度都会增加\(q\)(是一个非负整常数)。
蛐蛐国王知道这样不是长久之计,因为蚯蚓不仅会越来越多,还会越来越长。蛐蛐国王决定求助于一位有着洪荒之力的神秘人物,但是救兵还需要\(m\)秒才能到来……(\(m\) 为非负整数)
蛐蛐国王希望知道这\(m\)秒内的战况。具体来说,他希望知道:
\(m\)秒内,每一秒被切断的蚯蚓被切断前的长度(有\(m\)个数);
\(m\)秒后,所有蚯蚓的长度(有\(n + m\)个数)。

题解

先说一句,优先队列会被卡常TLE

单调性.
发现先被切掉的蚯蚓分成的蚯蚓一定比后切掉的蚯蚓分成的蚯蚓大.   
假设这两只蚯蚓分别为\(a,b\),其中\(a>b\).那么它被切成\(a_1,a_2\)\(t\)秒后, \(b\)被切成了\(b_1,b_2\),此时\(a_1,a_2\)的长度为\(l_{a_1}+t=pl_{a}+t,l_{a_2}+t=(1-p)l_a+t\),而\(b_1,b_2\)的长度却为\(p(l_b+t),(1-p)(1_b+t)\),容易看出\(l_{a_1}>l_{b_1},l_{a_2}>l_{b_2}\)。也就是说根本不需要用一个堆来维护, 它本来就具有一定单调性.
那么就是说如果蚯蚓\(a_1,a_2,\cdots,\)满足\(a_1>a_2>\cdots\),那么以此分成两只\(a_{11},a_{12},a_{21},a_{22},\cdots\)。那么\(a_{12}>a_{22}>\cdots,a_{11}>a_{21}>\cdots\),那么就可以将这两堆依次存储,加上还没被切过的蚯蚓.每次要切时在这三堆里面选择最大的, 切完再依次放回去. 所以这么做时间复杂度为\(O(m)\).再优化一下细节基本上就没问题了.

和合并果子思路一致,我们把原蚯蚓,切小段蚯蚓,切大段蚯蚓分别放三个队列, 则这三个队列单调。
比较恶心的是蚯蚓会长
那我们可以做懒标记啊(线段树大家学过吗?)

动态规划(一)

•活跃于各种考试之中
•出镜率特别高
•给每位考生带来噩梦的感受
•几乎不存在什么知识点
•但是有的时候就是死活想不出来

例题讲解

1.P1216 数字三角形

观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。

        7 
      3   8 
    8   1   0 
  2   7   4   4 
4   5   2   6   5

在上面的样例中,从\(7 \to 3 \to 8 \to 7 \to 5\)的路径产生了最大
求路径所能达到的最大值。

题解

一个错误的贪心:每次选择都走最大的格子。

一个想法,我们可以写一个函数d(i,j)表示从(1,1)走到(i,j)的最大路径是多少。
先不考虑边界问题,我们也不难写出一个内部实现的递归方程:

\[d_{i,j}=max(d_{i-1,j},d_{i-1,j-1})+a_{i,j} \]

但是当然,我们知道这样做复杂度会爆
由此我们思考我们是否需要求解那么多的\(d\), 有的\(d\)的值就不能存起来吗?
既然对于每个\(d_{i,j}\)都只跟\(d_{i-1,j},d_{i-1,j-1}\)有关,那么后二者确定下来前者就应该是不变的,因此搜索过程中d的值在更新完了之后就保持不变了。
也因此可以开一个数组记录\(d_{i,j}\),如果它已经被求过了那么直接返回值就好,就不用重复算了。
复杂度不难发现每个坐标只会被访问一边。
我们把递归的式子用递推写出来,

d[i,j]=max(d[i-1,j],d[i-1,j-1])+a[i][j];

也是正确的,并且复杂度比刚才更加显然。
而且减少了函数的调用。

2.POJ2533 求最长上升子序列

题意显然。
子序列:序列\(\{a_n\}\),则其一个子序列是存在一些数\(j_x\),使得\(1\le j_1<j_2<...\le n\),构成子序列\(\{a_j\}\)

题解

\(f_i\)表示以\(a_i\)结尾的上升子序列最长为多少。初始化显然每个\(f都为1\)。我们从\(1\)~\(i-1\)枚举\(f_j\),当\(a_j<a_i\)时,\(f_i=max(f_i,f_j+1)\)
最后我们输出最大的\(f_i\)即可。
复杂度为\(\text O(n^2)\)

3.POJ1458 最长公共子序列

给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到,而且每个字符的先后顺序和原串中的先后顺序一致。

题解

两个字符串我分别设为\(a,b\),他们的长度分别为\(n,m\),设\(f[i][j]\)表示\(a\)串的\(1\)$i$,$b$串的1j的LCS为多少。如果\(a[i]=b[j]\)的话,\(f[i][j]=f[i-1][j-1]+1\);否则\(f[i][j]=max(f[i-1][j],f[i][j-1])\) //这条的正确性很显然
复杂度为\(O(nm)\)

诶我式子写错了吗?

f[i][j]=max(f[i-1][j-1],max(f[i-1][j],f[i][j-1]) )

我猜你们认为这才对。事实上这个确实对,但我之前写的也对。为什么?
实际上,\(f[i-1][j]\)或者\(f[i][j-1]\)\(f[i-1][j-1]\)相比,要么相等,要么比它大1(以前者为例,比如说第\(i-1\)\(j\)匹配上了,就有可能存在比它大取得情况)
所以完全没问题。

4.LGP1006 传纸条

小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个\(m\)\(n\)列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 \((1,1)\),小轩坐在矩阵的右下角,坐标 \((m,n)\)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用\(0\)表示),可以用一个\([0,100]\) 内的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。现在,请你帮助小渊和小轩找到这样的两条路径。

题解

首先先明确,虽然是一次来一次回,但是完全可以变成两次来。
这道题解法并不唯一,我只讲一种。
\(dp_{i,j,k}\)表示两个纸条走\(i\)个格子,一个纵坐标到\(j\),一个纵坐标到\(k\),除起点和终点外不会走重,此时的和的最大值。
通过这个方程我们能求出当前两个纸条走到什么地方。
所以答案就是\(dp_{m+n-1,m,m}\)

\[dp_{i,j,k}=\max(dp_{i-1,j-1,k-1},dp_{i-1,j-1,k},dp_{i-1,j,k-1},dp_{i-1,j,k})+a_{j,i-j+1}+a_{k,i-k+1} \]

但是我们还有一个问题,即二者路径不能经过重复的人。
不难发现既然不允许重复用人,也就是说除\(dp_{0,0,0}\)\(dp_{m+n-1,m,m}\)外,所有的\(dp_{i,j,j}\)都是无效状态,任何状态转移方程都不能从它这里转移出去。
特判+预处理

5.区间dp典例:P1880

在一个圆形操场的四周摆放 \(N\) 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 \(N\)堆石子合并成\(1\) 堆的最小得分和最大得分。

题解

提示:给你状态f[i][j][0/1]表示将i~j堆石子合并为一堆的最小/最大得分
不难写出以下代码:

maxn[l][r]=max(maxn[l][r],maxnfl][k]+maxn[k+l][r]);
minn[l][r]=min(minn[l][r],minn[l][k]+minn[k+l][r]);

(这个式子少写了新一次合并的得分)
但是我们仍然还有问题要解决,首先如何处理“圆形操场的问题”?dp状态推导的枚举顺序是怎么枚举的呢?
【待补充】

5.树形dp典例:P1270

艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。小偷知道每个展室里藏画的数量与通过每条走廊的时间。他拿下一幅画需要5秒的时间。计算在警察赶来之前,他最多能偷到多少幅画并逃走。

题解

首先我们可以发现这是一棵二叉树。
我们设f[i][j]表示我们从i节点出发(只往它子树走),偷j幅画最后再回到i所花的最少时间。

f[i][j]=min(f[i][j],f[lson][k]+f[rson][j-k]+w)

w代表的是从i走到lson再回来以及从i走到rson再回来的时间。
注意如果有一个节点我们不需要偷任何画我们也就不需要走到那个节点了,自然要从w里扣除。
那么答案就是k尽可能大且f[1][k]小于警察来的时间。

posted @ 2020-03-10 11:51  刘子闻  阅读(194)  评论(0编辑  收藏  举报