动态规划基础

内容来自acwing。还没有全部写完,y总nb!

背包问题

就是有N个物品(有体积和价值两种属性)放进体积为V的背包,最终要使总价值最大

01背包问题:每个物品最多只能用一次(也就是0和1)

动态规划从两个方面考虑:状态表示——就是想想状态要用几维的变量表示,并且要想清楚变量和状态的含义是什么 f(i,j)
从两个方面考虑:一般是从表示的集合是什么(1、f(i,j)表示的是集合的某种属性,要想想这个集合代表什么,比如背包问题就是所有选法的集合,2、这个选法需要满足两个条件:1、只从前i个当中选 2、选出来的总体积<=j 集合就是所有满足某种条件的选法的集合)
还有要想表示的属性是什么(f(i,j)虽然是一个集合,但他存的是一个数(所有选法的价值的最大值),这个数就是集合的某种属性,是集合的最大值)属性一般有三种:最大值、最小值,数量
y总讲的就是从集合的角度理解动态规划
状态计算——就是如何能一步步把每一个f(i,j)算出来
状态计算表示的就是集合的划分(如何把当前的集合(就是指f(i,j))划分为几个更小的子集合,使得每个子集我们都能算出来) (划分的时候不重,就是每个子集都没有交集,也不漏,不漏一定要满足,但是不重的话看情况 一般按最后一个元素进行划分
背包问题中f(i,j)划分为2个子集 第一类是不含i的选法(其实就变成了f(i-1,j)),第二类是包含i的选法(曲线救国,先不选,为f(i-1,j-vi),再加上第i个物品的价值,就是f(i-1,j-vi)+wi)(要判断数组越界的情况)
所以最终的f(i,j)=Max(f(i-1,j),f(i-1,j-vi)+wi)
状态可以直接用二维数组表示,也可以优化到一维数组,就是做一些等价变换
dp问题的优化一般是对动态规划的代码或方程做等价变形 所以做的时候先不考虑优化
优化的时候因为i每次只会用到i-1,因此f[j]代表每一层的f[j],其实也就是f[i][j],因此可以直接将i这个维度删掉,f(j)=Max(f(j),f(j-vi)+wi)因为j-vi<j,之前被更新过,所以j-vi是第i层的,与原式不符,因此j改为从大到小遍历
如何判断是第i层还是第i-1层呢? for循环到i时,起初都是第i-1层的,更新过的是第i层的,没被更新过的是第i-1层的

完全背包问题:每个物品能用无限次

完全背包和01背包的差别主要在集合划分 这里分为若干组,按照第i个物品选几个来划分,从0…n,i为0的情况就是f[i-1][j],i不为0的部分依旧要曲线救国:1、先不选k个物品i 2、再加回来k个第i个物品 f[i][j] =max(f[i][j], f[i-1][j-k* v[i]] + k*w[i])
f[i][j] = max(f[i-1][j], f[i-1][j-v]+w, f[i-1][j-2v]+2w, .......) 上下长度是一样的
f[i][j-v]]=max( f[i-1][j-v], f[i - 1][j - 2v]+w, ........) 得到f[i][j] = max(f[i-1][j], f[i][j-v]+w) 跟01背包很像,可以直接优化成一维 j从小到大循环 不用背,先理解

多重背包:每个物品最多有Si个

大概思路和完全背包问题类似,只不过k的范围从kv[i]<=j变为了k<=s[i] && kv[i]<=j
f[i][j] = max(f[i-1][j], f[i-1][j-v]+w, f[i-1][j-2v]+2w, .....f[i-1][j-sv]+sw)
f[i][j-v]]=max( f[i-1][j-v], f[i - 1][j - 2v]+w, .....f[i-1][j-sv]+(s-1)w + f[i-1][j-(s+1)v]+sw 因此不能用完全背包的优化方式进行优化
s=1023 要枚举0~1023 相当于把第i个物品打包成这十组1, 2, 4, 8, …, 512,每组只能选一个,变成了01背包 枚举这十个新的物品选或不选,就拼出第i个物品所有的方案
0~200 用1, 2, 4,…, 64, 73
si->每个物品拆分成logsi个物品,因此就是n * logsi个物品的01背包问题

分组背包问题:物品有N组,每组里最多选一个

划分标准是第i组物品选哪个:不选,选第一个,选第二个…(选第k个)
f[i][j] = max(f[i-1][j],f[i-1][j-v[i,k]]+w[i,k])
优化后:f[j] = max(f[j], f[j-v[i,k]]+w[i,k])
背包问题优化:如果用上一层就从大到小枚举,如果用本层的就从小到达枚举

线性DP

指递推顺序有模糊的线性关系,可能是一维也可能二维
数字三角形:状态表示 f(i ,j) 集合:所有从起点走到(i, j)的路径 属性:最大值
状态计算:从左边来的+从右边来的 都减等于都没减 f(i, j) = max(f(i - 1, j - 1) + a(i, j), f(i - 1, j) + a(i , j))
一般涉及到i-1这种下标,i就从1开始,否则从0开始
动态规划时间复杂度:状态数量 X 转移计算量
最长上升子序列:状态表示 f(i) 集合:所有以第i个数结尾的上升子序列的集合 属性:最大值
状态计算:集合划分:倒数第二个数是几(a0(不存在) a1 … ai-1) f[i] = max(f[0]+1, …, f[i - 1]+1 if ai > aj) O(n^2)
动态规划问题求方案只要把转移存下来就行
优化在习题课
最长公共子序列:状态表示 f(i, j) 集合:所有在第一个序列的前i个字母中出现和第二个序列的前j个字母中出现构成的子序列 属性:最大值
状态计算:以a[i], b[j]是否包含在子序列当中作为划分依据,共有四种情况
00 f[i-1, j-1] 一般不用写因为f[i-1,j-1]同时包含在f[i-1,j]和f[i,j-1]中
01 f[i-1,j]这个状态与01不是完全匹配的,01包含在f[i-1,j]当中,但是是求max,因此可以用该状态替换01
10 f[i, j-1] 11 f[i-1, j-1]+1
优化:假如求到第i个数,前面的子序列按长度分类,只用存每种长度结尾最小的子序列 有些类似单调队列
不同长度上升子序列结尾值随着长度递增 -> 在序列中找到小于ai的最大的数(二分),更新这个序列 其实不算DP了
求max或min的时候集合划分之后的表示是可以重复的,只要不漏就行,但求数量的的时候不能有重复
最短编辑距离:状态表示 f(i,j) 集合:所有将a[1 ~ i] 变成b[1 ~ j]的操作方式 属性:最小值
状态计算:以对最后一个字母的操作进行划分 删a[i] f[i-1,j]+1 增 f[i,j-1]+1 改a[i] f[i-1,j-1]+1 a[i]不变 f[i-1,j-1]

区间DP

石子合并: 状态表示f(i,j) 集合:所有第i堆石子到第j堆石子合并成一堆石子的合并方式 属性:最小值
状态计算:以最后一次合并的分界线划分 都减等于都没减 f[i,j] = f[i,k]+f[k+1,j]+s[j]-s[i-1] 用前缀和

计数类DP

整数划分:可以看作完全背包问题,问恰好装满背包的方案数
状态表示f[i,j] 集合:所有从1-i中选,且总体积恰好为j的选法 属性:数量
状态计算:以第i个物品选择了几个进行划分 f[i][j] = f[i-1][j] + f[i-1][j-i] + f[i-1][j-2i]… + f[i-1][j-si])
优化方式和完全背包一样
计数问题:要分情况讨论——很重要的思想
DP中最难的有两步,一个是想出状态表示,一个就是分情况讨论

数位DP

某个区间里满足某种性质的数的个数:技巧一:把两个区间变成一个区间f([x, y]) -> f(y) - f(x-1) (f(x)是0~x)
技巧二:用树的角度进行考虑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值