刷题随笔
暑假开始,主要刷点题,防止忘得太严重,随便记录下基本思路。
还有不写线段树平衡树那些了,太费时间了,debug烧脑)。
codewar用java刷,其他的用C++。
- 单调栈
-
luoguP5788 : 给定一个数组,要求求出每个数组元素后面第一个比它大的元素下标。单调栈模板题,维护一个从栈底到栈顶单调减小的栈。逆序扫描数组,每次插入当前元素,并且维护单调栈性质,先维护后插入,插入前的栈顶元素就是对当前元素要找的大于它的第一个元素。
-
luoguP1901 :同上。
-
luoguP2866 :维护每头牛向右找到第一个不比自己矮的坐标,统计两牛之间的总数即可,这样是从右往左扫描。题解似乎还有一种更优的做法:与其求一头牛能看见几头,不如求本牛能被几头牛看见,能看见的总数一定等于被看见的总数,这样是从左往右扫描,单调栈中的就是大于当前位置的,也就是能看间当前位置的牛的集合。
-
SP1805:如果这题的矩形高度都是递增的,那么做法很简单。显然就是假设当前矩形为最终子矩阵的高度,向右扩展到边界即可。这也启发我们,如果矩形是递增的,我们完全可以放着不管,后面来处理,但是如果下一个矩形的高度变小了,那么以当前矩形的高度来构成矩形是不可以的,所以如果要加入下一个矩形,那么这些比下一个矩形高的矩形高度不能用来假设最终矩形的高度,那么我们可以不在加入下一个矩形的前提下把这些比下一个矩形高的矩形拿来计算,统计最大值即可。
具体做法就是从左往右读入矩形,维护单调栈,如果当前矩形比栈顶高,那么直接入栈,否则的话不断出栈,在此过程中,我们不断以被出栈的矩形的高度来计算一个极大矩形,为了快速计算,同时也维护一个累计宽度,如果这个栈顶矩形是在递增的时候加入的,那么显然这个累计宽度为1,因为递增的话就无法向左扩展,否则的话可以不断加上这些被出栈的累计宽度,因为当前矩形都比它们低,它们能向左扩展多少,当前矩形一定也可以。
法二:不妨假设当前矩形高度为最终矩形的高度,那么求出当前矩形向左扩展和向右扩展的最远距离即可,这两个最远距离可以通过预处理求得。这样的话也是线性的。
法二其实叫悬线法,很多题可以用悬线法解的,也可以使用法一单调栈法来做。 -
P4147 玉蟾宫:可以求出每个格子最多向上扩展几格,这样就能抽象成一个个宽度为1的矩形了,和上一题差别不大。就是枚举每一行作为矩形的下边即可。由上一题的线性变成了平方。
-
- 单调队列
- luogu2216:每行跑一次单调队列,然后再每列跑一次即可,注意边界判断,第一次跑的每行单调队列,相当于这个点为右边界的滑动窗口。第二次跑的每列单调队列,相当于以这个点为右下角的n*n方阵。
- luogu2698: 一开始想的是二分区间长度加单调队列,看下时间复杂度nlogn似乎能过。由于点的坐标取值范围很小,1e6级别,我直接用两个数组来存,因为一个x坐标可能会有多个雨水,那么a[x]存落在x的所有雨滴最早落下的,另外一个就存最晚落下的咯。然后就二分check。
法二是线性做法,因为要求最小的一个合法区间,不然发现一个点为左边界/右边界,都有一个唯一的合法的最小的右边界/左边界,然后我们可以类似虫子伸缩的方式,不然发现区间变小的话,最大值和最小值的极差会变小。反之会变大,所以每次向右扩展一个点,然后检查左端点,不断右移直到不合法(区间极差太小了),这样保证每个元素只会扫描一次,可一做到线性。
3.背包DP - 基本的01背包,每种物品有且有一个,那么可以选择放或者不放。考虑前i个物品放到容量为V的背包中,
\[f[i][v]=max \left\{ f[i-1][v],f[i-1][v-c[i]]+w[i] \right\} \]考虑第i个物品放或者不放,放或不放,分别从两个容量不同的背包继承来
时间复杂度是N*V无疑了,空间却可以优化为V。我们可以去掉第一维,相当于滚动数组优化,如果我们背包容量的循环采用V...0的顺序倒推的话,那么能保证求f[v]时,f[v-c[i]]的状态实际是f[i-1][v-c[i]]的值了。
初始化细节,如果要求恰好装满背包,那么初始化f[0]=0,其他的设为负无穷即可。如果不要求装满,只希望价值最大,那么全清0即可。可以这样理解,初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值0的东西恰好装满,其他容量均没有合法解,属于未定义状态,那么值就是负无穷,如果背包不一定要装满,那么任何容量背包都有一个合法解"什么都不装",价值为0。-
完全背包,每种物品有无穷件,不是放或不放的问题,而是放多少的问题。
如果考虑加一重循环来考虑放几件的话,或者将一种物品拆分为多件物品,还可以使用二进制拆分方法,将一种物品拆分成多个物品集合,每个集合都是2的次幂个相同物品的捆绑包,只要这个捆绑包的体积不超过V即可,这样这些捆绑包就能组合出不同件数的物品了,但时间复杂度都会超过VN。
完全背包有一个简单的优化,如果一件物品的体积大还价值小,明显可以去掉它,N^2解决。对于背包类问题,还可以先去掉大于背包体积的单件物品,然后计算出费用相同的物品中价值最高的是哪个,V+N解决。
如果把01背包的倒序计算改为顺序的话,那么就是完全背包了,在第i件状态时,容量V的状态也会考虑到已经选了第i件物品的V-c[i]的状态,变成了加选一件,这也是利用了局部最优子结构的性质重复了之前的枚举过程,优化了枚举的复杂度\[f[i][v]=max \left\{ f[i-1][v],f[i][v-c[i]]+w[i] \right\} \] -
多重背包。每种物品的数量不一样多为n[i],简单的思路就是拆分,二进制拆分1,2,4...2(k-1)加上n[i]-2k+1(k是满足这项>0的最大整数),这样就能保证能组合出0...2k-1件,2k...n[i]件的物品选择。此外还有VN的解法,是用单调队列优化,不会)。
-
luoguP1048:01背包模板
-
luoguP1802:01背包变形
-
luoguP2946:01背包变形,这题需要注意的是容量j会取模,取余数后会构成一个环,无法保证倒序枚举,所以要用两维的01背包+取模,即2维DP。每次将选第i件和不选的方案数累加起来。
-
luoguP1616:完全背包模板
-
luoguP2918:完全背包变形,考虑到可能会买超过目标磅数的但是花销更少,所以要多统计刚好超过目标磅数(最多5000)的答案。状态方程表示的含义有点变化。
-
luoguP1853:完全背包变形,相当于多次完全背包,每次的容量都有所增加。注意题目提示的1000的倍数这个特点,相当于可以用1,2,3,4映射到1000,2000,3000,减小空间
-
luoguP1776:多重背包模板
-
luoguP5365
本文来自博客园,作者:{2519},转载请注明原文链接:https://www.cnblogs.com/QQ2519/p/18283845

浙公网安备 33010602011771号