一、斜率优化:
若一个DP方程可以表示为f[i] = min(f[j] + cost(i, j)),假设j是最优选择,那么对于任意的k,都有f[j] + cost(i, j) >= f[k] + cost(i, j)。
若将上式移项得到左边可以表示为(g[j] - g[k]) / (h[j] - h[k]) ,右边是一个与j和k无关的式子,则可以用队列来优化DP。
队列维护的是一个下凸的图形,下凸就是斜率逐渐变大,证明略。
A - MAX Average Problem
B - Lawrence
二、四边不等式优化:
若一个DP方程可以表示为f[i][j] = min(f[i][k] + f[k + 1][j] + cost(i, j)),且对于cost(i, j)若固定j,cost(i, j + 1) - cost(i, j) 是一个关于i递减的函数,则s[i][j - 1] <= k <= s[i + 1][j]。
C - Tree Construction
三、序列上的DP:
D - Number String
序列上的DP很多是等价替换的思想。
对于这题,dp[i][j]表示第i个位置填j(1 <= j <= n)且满足要求的序列有多少个。但是这样表示是有后效性的。
换一种表示方法:dp[i][j]表示第i个位置填j(1 <= j <= i)且满足要求的序列有多少个。考虑dp[i - 1][k],若把这序列上大于等于j的的数字全部加1,这个序列仍然是合法的,但是j却消失了,i却用掉了(因为原来序列有i-1)。
E - Reverse the prefix
f[i][j]表示前i个字母采取最多j次翻转得到的最小序列。g[i][j]表示前i个字母采取最多j次翻转得到最大的序列。
转移比较简单。
四、单调队列优化
可以用单调队列优化的DP一般是用于限定长度的区间,且区间有整体移动的趋势。即每一个元素是逐次进出区间,且只有一次。队列里的值是单调的,一般dp[i]会直接等于Q.front() + cost;
F - Cut the Sequence
显然,dp[i] = dp[j] + max(a[j + 1], a[i]),其中且sum(j + 1, i) <= m。
但是此题dp[i]不一定等于Q.front() + cost,因为cost是动态的且不与队列的单调性一样。
一个最优的选择j,a[j]必定大于后面所有数(自己想)。
五、数位DP
G - Balanced Number
反正我用数位DP做挂了,网上的都是暴力深搜的。
六、概率DP
我概率DP渣渣啊,主要是状态的表示方法太灵活了。递推方式有从结果出发和从始态出发两种。而且往往会出现DP[i]用到DP[j],DP[j]用到DP[i]这样的情况,常用解决的方法有迭代求解和高斯消元。
H - Activation
I - Maze
这两题都是用的迭代法,H题略。
I题对于当前的点,dp[i] = A * dp[1] + B * (dp[father] + sigma(dp[son])) + C → dp[i] = A * dp[1] + B * dp[father] + B * sigma(dp[son]) + C → dp[i] = A * dp[1] + B * dp[father] + D
对于每一个节点,用一个三元组(A,B,D)来表示,然后不知道怎么说,就是从儿子的ABD更行自己的ABD,看代码吧。
七、斯坦纳树
最小生成树是斯坦纳问题的一种特殊情况。给N个点和相应的边,选定K个点,问如何选择边使得这K个点联通并且边权和最小。
dp[i][s]表示以i为根,使得k个点的联通情况为s的最小边权和。dp[i][s] = min(dp[i][p | st[i]] + dp[i][(s - p) | st[i]]); 然后spfa()
J - Peach Blossom Spring