Bootstrap

日常做题笔记

有的是洛谷老师布置的题目,有的是 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,k1×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,i1 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,i1=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

;