有的是洛谷老师布置的题目,有的是 CF 随机跳题。
并不一定是顺着排的。但是更新 50 50 50 题之后会根据这个博客的顺序列一个新的题单。欢迎点赞()
1. P1040 [NOIP2003 提高组] 加分二叉树
题意很明确。
不难发现这个分数的形式和数据范围很像区间啥的,那么考虑区间 DP。状态就好定义, f i , j f_{i,j} fi,j 表示前序遍历里 [ l , r ] [l,r] [l,r] 这段能得到的最大得分。转移方程也很显然,根据题目来就好了:
f i , j = max k = i j f i , k − 1 × f k + 1 , j + d k f_{i,j}=\max_{k=i}^{j}f_{i,k-1}\times f_{k+1,j}+d_k fi,j=k=imaxjfi,k−1×fk+1,j+dk
最初状态为 f i , i = d i f_{i,i}=d_i fi,i=di,最终答案显然为 f 1 , n f_{1,n} f1,n。
注意一个问题:由于这里 k k k 的范围是 i i i 到 j j j,那么可能会出现类似 f i , i − 1 f_{i,i-1} fi,i−1 或 f i + 1 , i f_{i+1,i} fi+1,i 的东西,而这东西默认是 0 0 0,那么此时我们应当将这些东西当作题目中的“空结点”来算,也就是 f i , i − 1 = f i + 1 , i = 1 f_{i,i-1}=f_{i+1,i}=1 fi,i−1=fi+1,i=1。这样就可以算出最大值了。
前序遍历也好算:对于一个区间 [ l , r ] [l,r] [l,r],记 m x l , r mx_{l,r} mxl,r 为在对 f l , r f_{l,r} fl,r 找最大值的时候找到最大值对应的 k k k。那么最后从 m x 1 , n mx_{1,n} mx1,n 一层层递归回去,就能得到前序遍历了。时间复杂度 O ( n 3 ) O(n^3) O(n3),可以通过。
int n; cin >> n;
for(int i = 1;i <= n;i++) cin >> d[i], f[i][i] = d[i];
for(int i = 1;i <= n;i++) f[i][i-1] = 1, f[i][i+1] = 1;
for(int len = 2;len <= n;len++) {
for(int i = 1;i + len - 1 <= n;i++) {
int j = i + len - 1; for(int k = i;k <= j;k++) {
if((f[i][k-1] * f[k+1][j]) > (f[i][j] - d[k])) {
f[i][j] = (f[i][k-1] * f[k+1][j]) + d[k]; mx[i][j] = k;
}
}
}
}
2. P2015 二叉苹果树
大蛇题。有点像背包?
这是一个树形的结构。(废话题目里都写了
那么考虑树形 dp,由于要求保留 Q Q Q 根树枝,求最大苹果数,那么不难想到用 f i , j f_{i,j} fi,j 表示在以 i i i 为根的子树中保留 j j j 根树枝所能得到的最大苹果数。接下来枚举左子树保留几根树枝即可。这里假设保留 k k k 根。
-
k = 0 k=0 k=0,此时左子树啥都不保留。
显然有转移方程 f i , j = f rson i , j − 1 + v i , rson i f_{i,j}=f_{\operatorname*{rson}_i,j-1}+v_{i,\operatorname*{rson}_i} fi,j=frsoni