算法笔记:DP(总)

本文介绍动态规划的三种主要类型:区间DP、背包DP及DP优化方法。详细解释了每种类型的适用场景、时间复杂度,并通过实例说明如何进行状态定义与转移方程设定。

一.区间DP

dp[l][r]:l,r为左右端点

1.基于左右两端点的可能性讨论

时间复杂度一般为O(n^2)

2.基于区间划分点的可能性讨论

时间复杂度一般为O(n^3)


3.前两种混合

二.背包DP

dp[i][j]:i为物品种类,j为背包属性(重量等)

1.背包问题一般先枚举物品种类,再枚举其他变量。

因为先枚举物品种类,会使递推式中物品种类选择是固定的。(比如先枚举的1物品,在2物品枚举之前,不会出现2物品,只有1物品)
而先枚举比如背包重量,其中的物品种类是不确定的。(比如枚举到重量为k,其中的物品种类你无法确定)
因此先枚举物品种类可以确保不出现重复问题。

2.分类:

设当前物品重量为k

01背包:

if j >= weight[i]:
    dp[i][j] = max(不选当前物品: dp[i-1][j], 
                    选当前物品: dp[i-1][j-weight[i]] + value[i])
else:
    dp[i][j] = dp[i-1][j]  # 无法选当前物品


01背包滚动数组优化:

for i in range(0,n-1):          # 遍历物品
    for j in range(W,weight[i]) :        # 逆序遍历容量
        dp[j] = max(dp[j], dp[j-weight[i]] + value[i])
 

完全背包:dp[i][j] = max(dp[i][j],dp[i][j-k])(枚举j)

多重背包,将每个物品数量转化为二进制,dp式与01背包一样

分组背包:01背包套01背包

子集和、等和分割、目标组合数等问题均可转化为背包模型

三.最简易的DP优化——滚动数组优化

正序与逆序

注意,正序或逆序取决于DP式传递的方向,下面使用dp[i] = dp[i-k]*(……)为例

如果正序,则会出现覆盖的情况

dp[i] = dp[i-k]*(……),dp[i]已经变成了一个新的值。dp[i+k] = dp[i]*(……),而由于dp[i]已经被新的值覆盖导致错误。

而逆序能保证不出现覆盖


因此01背包是逆序
而完全背包是正序(因为完全背包本身就允许物品覆盖,因为一件物品可以使用无限多次)

四.树形dp

树形dp可以解释为:用子问题的解来解决父问题。

树形dp维护以当前节点为根的树(子树)问题的解。

树形dp要依次遍历当前节点的每个孩子,依次得到每个子树的解。

根据如何处理每个子树,树形dp常见的可以有:

01背包树形dp:每个子树要么选要么不选

分组背包树形dp:当前节点有k个可分配元素,每个子树只能分配一个值,返回一个值

普通树形dp:每个子树得到的解都要,当遍历到第i个孩子(子树)时,之前所有子树的解记录在dp[i-1][……]中

五.状态dp

用二进制表示状态,用状态递推。

注意开始要选好对应顺序,可以正序也可以逆序,个人习惯用逆序。

原数组:0111,正序:7(0111),逆序:14(1110)。

状态dp常用函数:

注意:以下所有函数都是逆序对应的,且标号从0开始

//以下所有函数都是逆序对应的,且标号从0开始

//修改某一位
int change(int x,int i,int k){
    x=x&(~(1<<i));//将第i为设为0
    x = x | (k << i); // 将第i位设为k
    return x;
}

//判断当前状态是否存在相隔的距离小于k的1
//k==1时,就是判断是否存在相邻的1
bool judge(int x,int k){
    return !(x&(x<<k))&&!(x&(x>>k));
}

//得到某一位的状态
int get(int x,int k){
    return (x>>k)&1;
}

轮廓线dp

原理

假设n*m网格,来到(i,j)

状态信息的(0,j-1)表示i行的决策

状态信息的(j,m-1)才表示i-1行的决策

可以形象的想成:有一条轮廓线将状态分为新状态和老状态,同时轮廓线不断前进,新状态将不断替换老状态。

通过递推轮廓线的方式,轮廓线dp时间上优于普通状态dp

时间复杂度

状态dp需要穷举所有上一状态以及所有可能的当前状态,因此时间复杂度为

O(2^m*2^m*f(x))

m是状态的位数,f(x)是多项式函数

而轮廓线dp在穷举所有上一状态后,不需要穷举所有可能的当前状态,而是递推轮廓线,仅需要遍历状态的每一位,时间复杂度为

O(2^m*m*f(x))

m是状态的位数,f(x)是多项式函数

空间复杂度

轮廓线dp比普通状态dp多出一维,用于记录轮廓线的位置。

经常需要用滚动数组优化空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值