qbzt培训整理系列之dp
此系列并木有按照顺序来,前面的有时间补上
跟我读:咕咕咕
--------这里是手动分割线----------
DP做题步骤
区间DP
经典题之能量项链
变环为链,然后搞正常的区间\(dp\)
不过还有一种做法是设\(dp[i][len]\)表示从\(i\)往后推\(len\)个位置这一段合并的最大值
但本质都是一样的
感觉难一点的矩阵取数游戏
我们发现行与行之间的取数是互不影响的,也就是说每一行可以独立处理
辣么我们就独立处理每一行的最大得分,最后相加就可以了
设\(f[i][j]\)表示当前行被取的只剩下\([i,j]\)的时候的最大得分
\(f[i][j]\)可以由\(f[i-1][j]\)和\(f[i][j+1]\)转移而来,而且取走\(i-1\)或者是取走\(j+1\)时乘的数是固定的,为\(2^{m-j+i+1}\)
\(\therefore f[i][j]=max\{f[i-1][j]+2^{m-j+i+1}\times a[i-1],f[i][j+1]+2^{m-j+i+1}\times a[j+1]\}\)
\(f[1][m]\)初始化为0,最大的\(f[i][i]+a[i]\times 2^m\)加入答案
背包好题
飞扬的小鸟
设\(f[i][j]\)表示在点\((i,j)\)需要的最少点击次数
我们考虑是怎么到达当前位置的:
1.在上一列点了一下飞进来的
2.在本列点了若干下飞上来的
3.上一列不点,掉下来的
其中2的情况也可以认为是在上一列点了若干下,飞上来的。在转移的时候我们可以看做是先飞到\((i,j-x[i-1])\),再飞到\((i,j)\)
因为往上飞的次数是无限的,所以我们可以视作完全背包,而往下掉只能有一次,所以往下掉就是01背包
往上飞:\(f[i][j]=min\{f[i-1][j-x[i-1]],f[i][j-x[i-1]]\}\)
处理在\((i,m)\)的情况:\(f[i][m]=min\{f[i][j]\},j\in [m-x[i-1],m]\)
往下掉:\(f[i][j]=min\{f[i][j],f[i-1][j+y[i-1]\}\)
一些细节:被水管挡住的位置要在转移完之后赋值为正无穷表示不可到达
匹配dp
其实最长公共子序列就是个匹配\(dp\)
子串
这里选出的子串虽然不可以重叠,但是它们可以相邻
比如选出一串子串\(abb\)你可以说这是3个子串,也可以说是2个子串,还可以说是1个子串
我们模仿最长公共子序列,设\(f[i][j]\)表示\(A\)的前\(i\)位和\(B\)的前\(j\)位匹配的方案数
似乎少了点什么.........
好像把\(k\)给忘了
如果考虑选出的子串个数,就要考虑\(A\)的第\(i\)位是否选
那么设\(f[i][j][k][0/1]\)表示\(A\)的前\(i\)位中选出\(k\)个子串,其中第\(i\)位是/否在第\(k\)个子串中,与\(B\)的前\(j\)位匹配的方案数
考虑转移
若\(A[i+1]!=B[j+1]\)
反正\(A[i+1]\)是不能塞进第\(k\)个子串里了,那么\(f[i+1][j][k][0]+=f[i][j][k][0]+f[i][j][k][1]\)
若\(A[i+1]==B[j+1]\)
选\(A[i+1]\):塞进第\(k\)个子串或者是新建一个子串:
\(f[i+1][j+1][k][1]+=f[i][j][k][1]\)
\(f[i+1][j+1][k+1][1]+=f[i][j][k][1]+f[i][j][k][0]\)
不选\(A[i+1]\):
与不相等的情况相同,\(f[i+1][j][k][0]+=f[i][j][k][0]+f[i][j][k][1]\)
然后我们计算一下复杂度
时间复杂度:\(O(nmk)\),能苟住
空间复杂度:\(8\times 10^7\)
噫好空间炸了
于是我们把\(i\)这一维用滚动数组滚掉
一些我不会分类的dp
看到这题,我们可以先来一波直(xia)觉(cai)推测:
\(b\)应该是\(a\)的子集叭
而且\(b\)里面的元素应该是\(a\)中不能被除它自己以外的其他元素表达出来的元素,换句话说,就是把\(a\)能被重复表达的元素删掉
那么咱的直(xia)觉(cai)到底对不对呢
证一下叭
如果\(b\)中某一元素\(x\)不在\(a\)中:
如果\(x\)不能被\(a\)中的元素表达,则会\(x\)可表达\(a\)不能表达的新元素,不合题意
如果\(x\)可以被\(a\)中的元素表达,那么表达\(x\)的元素一定比\(x\)小,这些元素可以组合出\(x\)所不能表达的更多元素,所以还是不合题意
\(\therefore b\)是\(a\)的子集
如果\(b\)中有可以被重复表达的元素,那么删掉之后\(m\)会更小,所以\(b\)是\(a\)把所有能被重复表达的元素删掉之后的集合
辣么我们就可以\(dp\)看看哪些元素会被重复表达了
设\(f[i]=0/1\)表示面值\(i\)能否被小于它的面值表达出
初始化:\(f[0]=1\),其余为0
将\(a\)数组从小到大排序,第一层枚举\(a[i]\),第二层枚举\(x\),\(f[x+a[i]]|=f[x]\)
然鹅数据被毒瘤地加到了1e6
也就是说消防的\(nlogn\)的做法也被卡掉了
真·毒瘤
这里直径上的每个点可以将树分成若干个以直径上的点为根的子树,这样就可以预处理出每个点分得的子树的最长距离(树形dp即可)
那么直径上每个点的点权可视作这个点分得的子树的最长距离
我们要求的是选中的路径上的点的点权+两侧距离的最大值最小
如果他不卡\(nlogn\)我们还可以二分对不对
但是他卡了
我们发现某个点到两侧的距离似乎可以\(O(1)\)求
而且根据直(xia)觉(cai),核的长度肯定是越接近\(s\)越优
辣么就是不断维护长度为\(s\)(或者接近\(s\))的路径上的最大值,看看最小是多少
因为长度固定,所以我们可以单调队列解决
to be continued