算法基础——动态规划

动态规划(Dynamic Programming)

动态规划的模板

1、 设计状态
2、 写出状态转移方程
3、 设定初始状态
4、 执行状态转移
5、 返回需要时刻状态的解

DP的核心在于状态只和上个(或几个)的状态有关,和转移到该状态的路径无关,和之后的状态也无关,本质是一个有向无环图,状态是结点,关系是边,从开头往结尾转移。
关于起点的初始化,个人理解是往前倒推,直到某个值没法推了,需要按任务要求直接赋值,还有一些问题需要对整个数组初始化,如图的各自路径任务,看具体任务需求,有的要初始化为0,有的要初始为INF,有的无所谓等等。
动态规划处理大量数据(结点)效率不高,所以如果数据量n很大没法映射到数组(结点)中的话,一般不是动态规划问题(至少不是最优解),可能是贪心或者用二分优化等等。

动态规划的关键点

dp[i]的含义,递推公式,dp的初始化,遍历顺序/方向。
如果定义好dp但不知道递推公式,可以找个样例把dp数组手动推出来观察,笨但也许有效。所以最最关键的是dp的定义。
动态规划debug可以打印dp数组观察状态变化,分析是思路问题还是代码问题。
线性DP:
时间复杂度只和状态数成线性关系,O(n);
而状态转移与n无关,只需要O(1);

动态数组的优化

空间优化

动态规划的主要优化内容。
利用滚动数组思想做状态压缩。当我们定义的状态在动态规划的转移方程中只和某几个状态相关,且这些状态可以与某个维度无关,且任务后续不需要再使用之前的状态,就可以用这种方法把那个维度压缩掉,给空间复杂度降低一维,只维护我们单次转移需要的空间数量即可,尤其是当数据量、维度很大的时候。
利用滚动数组隐藏了一个维度的特性,在dp初始化时可以更自由,比如某些递推公式从数学上可以推导数组下标为负的情况,二维数组初始化下标i只能从0开始,有些可能还要手动算几步,而滚动数组的递推i可以从-1甚至更前面开始,也许默认0就可以,或者只初始化dp[0],这样下标含义的灵活度更高。
注意有些滚动数组压缩还伴随着剩余维度倒序遍历,因为原本正序的递推公式为了不破坏前面的数组,需要从后往前更新。
没法完全忽略压缩维度到1,还可以考虑用交替数组。

时间优化

通常可以写成贪心的DP,贪心的复杂度一定<=DP,滚动数组可以使DP的空间复杂度和贪心一致,而想要优化时间可以尝试改变dp状态的定义,最常用的方法是按贪心的思想将dp的状态和状态值含义交换(不是简单的直接交换),如dp[i]的值原本表示以nums[i]为结尾的最长子序列长度,改成长度为i的最长子序列末尾元素的最小值。好吧,其实本质上就是转换成贪心了,所以如果有说明可以优化时间,通常就是让你改成贪心,贪心的思路可以从调换dp含义去考虑。

背包问题

01背包

即每种物品只有一个,只有选0个和选1个两种状态,总状态为2的n次方,也是暴力枚举的复杂度。01指的是每种物品数量只有0和1两种状态,并非物品的限制维度。

0. 完整代码
int solve(vector<int>& weight, vector<int>& value, int m) {
   
	int n = weight.size();
	// if (!n || !m) return 0; // 如果考虑异常输入
	vector<vector<int>> dp(n, vector<int>(m + 1, 0));
	// dp[i][j]代表i个物品装进背包空间为j的最大价值,注意j是能等于m的,所以列是m+1
	// 初始化两条边dp[i][0]、dp[0][j],其他的无所谓
	for (int i = 0; i < n; i++) {
   // 这个可以省去,默认是0
		dp[i][0] = 0;
	}
	for (int j = weight[0]; j < m + 1; j++) {
   
		// 从背包空间大于weight[0]开始赋值,前面都是默认0
		dp[0][j] = value[0];
	}
	//递推公式
	for (int i = 1; i < n; i++) {
   
		//先遍历物品大小,01背包问题先后都行,因为该递推公式任意遍历顺序都可以得到当前dp
		for (int j = 1; j < m + 1; j++) {
   //再遍历背包大小
			if (j < weight[i]) dp[i][j
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值