【DP动态规划】学习笔记大全

2 篇文章 0 订阅

-------------------------------------------------------本篇文章尚未完结,大家可以先看已有部分-------------------------------------------------------


在这里插入图片描述

Part 1 背包DP

1.1 01背包

1.1.1 题意解释

你有一个容量为 n n n 的背包,一共有 m m m 种物品,每个物品都有一个权值 v i v_i vi 和体积 w i w_i wi,在不超过背包容量的前提下,尽可能装权值最多的东西

1.1.2 为什么不使用贪心

考虑到本题,没学过DP的人可能会用以下两种贪心:

  1. 装权值越大的东西:(错误)
i i i123
v i v_i vi432
w i w_i wi70691

对于上面这一组数据,当 n = 70 n = 70 n=70 时如果我们使用这样的贪心策略,会优先选择体积为 70 70 70,权值为 4 4 4 的物品,但明显的,选择另外两个会更优。

  1. 计算平均值,在许可范围内选择平均值最大的
i i i123
v i v_i vi150100100
w i w_i wi1007574
v i w i \frac{v_i}{w_i} wivi 3 2 \frac{3}{2} 23 4 3 \frac{4}{3} 34 50 37 \frac{50}{37} 3750

明显的,对于上述样例,这种方法依然得不到正确答案。

1.1.3 该如何进行 01DP

我们设 d p i , j dp_{i,j} dpi,j 表示当前取到了第 i i i 个物品, 还剩 j j j 点容量时的最大值,那么我们即可得到一个转移方程:

d p i , j = m a x ( d p i − 1 , j − w i + v i , d p i − 1 , j ) ; dp_{i,j} = max(dp_{i - 1,j - w_i}+v_i,dp_{i - 1,j}); dpi,j=max(dpi1,jwi+vi,dpi1,j);

换一句话说,这就表示“把第 i i i 个,第 i + 1 i+1 i+1个,…, n n n个物品装到容量为 j j j 的背包中的最大权值”。

那么我们根据以上转移方程就可以得到以下代码:

 for(int i=1;i<=m;i++) {
        for(int j=t;j>=0;j--)  {
            if(j>=w[i])
                dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]); 
            else
                dp[i][j]=dp[i-1][j];           
        }
 }

明显的,答案就是 d p m , n dp_{m,n} dpm,n

1.1.4 推荐例题

P1048 [NOIP2005 普及组] 采药
P1049 [NOIP2001 普及组] 装箱问题
P2871 [USACO07DEC] Charm Bracelet S
P1060 [NOIP2006 普及组] 开心的金明
P1164 小A点菜

1.1.5 优化方式

对于 01背包,因为 DP 的无后效性,所以我们可以考虑用滚动数组进行优化,那么这个转移方程就变成了:

f j = m a x ( f j , f j − v i + w i ) f_j = max(f_j,f_j−v_i+w_i) fj=max(fj,fjvi+wi)

这样就可以省下大幅的空间。
注意:使用滚动数组时要倒序遍历


1.2完全背包

1.2.1 题意解释

  • 完全背包与 01背包类似,只不过每个物品可以选择无限多次。

1.2.2 如何进行状态转移

因此我们考虑任然使用 01 背包的状态设计,但明显的是,01背包的状态转移已经不适用于完全背包。
我们发现,对于 d p i , j dp_{i,j} dpi,j,只要从 d p i , j − w i dp_{i,j - w_i} dpi,jwi转移即可,这样满足了DP的无后效性,那么我们可以得到以下的状态转移公式:

d p i , j = m a x ( d p i − 1 , j − w i + v i , d p i − 1 , j ) ; dp_{i,j} = max(dp_{i - 1,j - w_i}+v_i,dp_{i - 1,j}); dpi,j=max(dpi1,jwi+vi,dpi1,j);

同样的,完全背包也可以像 01背包一样压缩掉一维,优化空间复杂度,同时,在压缩之后的转移顺序为正序,由此我们可以得到以下代码:

 for (int i = 1; i <= n; i++)
    for (int l = w[i]; l <= m; l++)
      if (f[l - w[i]] + v[i] > f[l]) f[l] = f[l - w[i]] + v[i];

1.2.3 推荐例题

P1616 疯狂的采药
P2722 [USACO3.1] 总分 Score Inflation
P1679 神奇的四次方数
P1832 A+B Problem(再升级)

Part 2 区间DP

2.1典例引入

  • 给定长度为 n n n 的序列 a a a,每次可以将其中连续的一段回文子段删去,删去后左右两段将合并,问最少用几次操作可以消除整个区间
  1. 经观察发现,每个时刻消去的位置总是一个连续的区间。
  2. 因此我们考虑消去区间 [ i , j ] [i,j] [i,j] 时:
  • a i a_i ai a j a_j aj 不在一起消去,那我们总是可以找到一个分界点 k k k ,使得区间 [ i , k ] [i,k] [i,k] 和区间 [ k + 1 , j ] [k + 1,j] [k+1,j] 可以分别消去。
  • a i a_i ai a j a_j aj,那他们只需要接在 [ i + 1 , j − 1 ] [i + 1,j - 1] [i+1,j1] 这段区间后一起消去即可
  1. 那我们用 f i , j f_{i,j} fi,j 表示消去这段区间需要的最少次数,那我们可以得到以下转移方程:
  • { f i , j = m i n ( f i , k + f k + 1 , j ∣ i ≤ k < j ) a i ≠ a j f i , j = m i n ( m i n ( f i , k + f k + 1 , j ∣ i ≤ k < j ) , f i , j , f i + 1 , j − 1 ) a i = a j \begin{cases} f_{i,j} = min(f_{i,k} + f_{k + 1,j}|i \leq k < j) & a_i \ne a_j \\ f_{i,j} = min(min(f_{i,k} + f_{k + 1,j}|i \leq k < j),f_{i,j},f_{i + 1,j - 1}) & a_i = a_j \end{cases} {fi,j=min(fi,k+fk+1,jik<j)fi,j=min(min(fi,k+fk+1,jik<j),fi,j,fi+1,j1)ai=ajai=aj
  1. 我们发现,这种状态转移是以区间为基础的,这种DP通常称作区间DP
  2. 区间DP的解法一般固定,一般是枚举区间长度,然后枚举左端点,最后枚举区间断点。时间复杂度为 o ( n 3 ) o(n^3) o(n3)

2.2 基本概念

  1. 合并:将两个及以上的区间通过一定方式进行整合,也可以反过来操作。
  2. 特征:能将问题分解成两两合并的形式。
  3. 求解:对整个问题设最优值,枚举合并点,将问题分解成左右两部分,最后合并两部分的最优解得到原问题的最优解,有点类似于分治的思想。

2.3 例题分析

本处采用石子合并作为例题分析。

2.3.1 题意解释

  • n n n 个石子沿着一个环分布,现在要将石子有次序的合成一堆。
  • 规定每次只能选择相邻的石子,并将新的一堆的石子数计为该次合并的得分
  • 要求得到两种合并策略,使得最后的得分最大和最小
  • 1 ≤ n ≤ 200 1 \leq n \leq 200 1n200

2.3.2 环的处理

考虑到题目中出现了环,那么我们该如何处理呢?
我们可以将链延长两倍,扩展成 2 × n − 1 2 \times n - 1 2×n1堆,其中第 i i i 堆与第 n + i n+i n+i堆完全相同,然后我们再转以后分别枚举 d p i , n , d p 2 , n + 1 , . . . , d p n , 2 n − 1 dp_{i,n},dp_{2,n+1},...,dp_{n,2n-1} dpi,n,dp2,n+1,...,dpn,2n1,并取其中的最大值即可。
时间复杂度是 O ( 8 n 3 ) O(8n^3) O(8n3) 符合本题的要求,一般是区间DP出现环时最好的处理方法

2.3.3 题目思路

  1. 考虑到如果第 l l l 和第 r r r 堆石子被合并,那么 [ l , r ] [l,r] [l,r] 之间的石子应当也被合并,所以我们可以在任何时刻用 [ l , r ] [l,r] [l,r] 这一个区间来表示任意一堆石子,代表他们是由 [ l , r ] [l,r] [l,r]之间的石子所合并来的。
  2. 同时也存在一个 k k k,使得 [ l , k ] [l,k] [l,k] [ k + 1 , r ] [k+1,r] [k+1,r]之间的石子能分别被合并。
  3. 那么我们就可以用 f i , j f_{i,j} fi,j 表示从第 i i i 堆石子合并到第 j j j 堆石子的最大(最小)值,通过对左右端点对区间的表示,我们可以得到以下转移方程(以最大值为例):
  • f i , j = m a x ( f i , k + f k + 1 , j + s u m j − s u m i − 1 ∣ i ≤ k ≤ j − 1 ) f_{i,j} = max(f_{i,k} + f_{k + 1,j} +sum_j - sum_{i - 1}|i \le k \le j - 1) fi,j=max(fi,k+fk+1,j+sumjsumi1ikj1)

在此处 s u m i sum_i sumi 表示从第 1 1 1 堆石子到第 i i i 堆石子数的总和。

2.3.4 主要代码

for(int p=1;p<n;p++) {  
        for(int i=1,j=i+p;(j<n+n) && (i<n+n);i++,j=i+p)  {  
            f2[i][j] = 999999999;  
            for(int k=i;k<j;k++)  {  
                f1[i][j] = max(f1[i][j], f1[i][k]+f1[k+1][j]+d(i,j));   
                f2[i][j] = min(f2[i][j], f2[i][k]+f2[k+1][j]+d(i,j));  
            }  
        }  
    }  

2.4 推荐例题

P1063 [NOIP2006 提高组] 能量项链
P3146 [USACO16OPEN] 248 G
P4767 [IOI2000] 邮局 加强版

Part 3 树形DP

3.1基本概念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值