动态规划总结练习(算法笔记)

本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。


一、最小消耗能量

在这里插入图片描述
搜索肯定超时,动态规划求解如下:
状态:dp[i]表示处在第i个高台时消耗的最小能量;
状态转移方程:
dp[i] = max(dp[i + 1] + abs(h[i] - h[i + 1]), dp[i + 2] + abs(h[i] - h[i + 2]));
可以画出树状图,然后递推从边界求解,算是最原始的动态规划,和斐波那契差不多。
边界处理:
dp[n - 1] = 0;
dp[n - 2] = abs(h[n - 1] - h[n - 2]);
完整代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
 
const int MAXN = 10e4;
int h[MAXN] = {};
int dp[MAXN] = {};

int main(){
	int n;
	scanf("%d", &n);
	for(int i = 0; i <= n - 1; i++)
		scanf("%d", h + i);
	dp[n - 1] = 0;
	dp[n - 2] = abs(h[n - 1] - h[n - 2]);
	for(int i = n - 3; i >= 0; i--)
		dp[i] = min(dp[i + 1] + abs(h[i] - h[i + 1]) , dp[i + 2] + abs(h[i] - h[i + 2]));
	printf("%d", dp[0]);
}

二、矩阵最大权值

在这里插入图片描述
搜索肯定超时,动态规划求解如下:
状态:dp[i][j]表示处在i行j列时路径上的最大权值之和;
状态转移方程:
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]) + matrix[i][j];
这个问题和数塔类似,算是比较简单了。
完整代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;

const int MAXN = 100 + 1;
int matrix[MAXN][MAXN];
int dp[MAXN][MAXN] = {0};

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            scanf("%d", &matrix[i][j]);
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]) + matrix[i][j];
        }
    }
    printf("%d", dp[n][m]);
    return 0;
}

三、非连续数位序列

在这里插入图片描述
这个是序列问题,所以肯定需要一个维度i来表示序列下标,代表以i为结尾的不连续0的序列数,但一个维度还不能把状态写出来。这个问题可以分解为两个子问题,即当前位置放0或者非0,而子问题的选择依赖于i-1位置上的元素是0还是非0,所以需要再开一个维度来存储当前位置上元素的选择是0还是非0,故状态设计为dp[i][v],状态转移方程为:
dp[i][v] = dp[i - 1][1] (v= =0)
dp[i - 1][0] + dp[i - 1][1]) * 9 (v= =1)
其中,当v为0时代表当前位置为0,当v为1时代表当前位置为非0,将结果对10007取模。
这个没有题解,所以也不知道这样写算不算标准······
完整代码如下:

#include<cstdio>
const int MAXN = 10e4;
int dp[MAXN][2] = {};

int main(){
	int n;
	scanf("%d", &n);
	dp[0][0] = 1;   //状态边界
	dp[0][1] = 9;
	for(int i = 1; i <= n - 1; i++)
		for(int v = 0; v <= 1; v++){
			if(v == 0) dp[i][v] = dp[i - 1][1] % 10007;
			else dp[i][v] = ((dp[i - 1][0] + dp[i - 1][1]) * 9) % 10007;
		}
	printf("%d", (dp[n - 1][0] + dp[n - 1][1]) % 10007);
}

四、最小涂色成本

在这里插入图片描述
这道题和上道题类似,属于“连续性”问题,也是用二维状态dp[i][j],i表示前i张纸可以构成的最小成本,j表示当前位置的纸张颜色的选择。
状态转移方程为:
dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + a[i];
dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + b[i];
dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + c[i];
其中j=1,2,3代表三种颜色,分析方法与上道题类似。
完整代码如下:

include <cstdio>
#include <algorithm>
using namespace std;

const int MAXN = 10000;
int a[MAXN], b[MAXN], c[MAXN];
int dp[MAXN][3];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d%d%d", &a[i], &b[i], &c[i]);
    }
    dp[0][0] = a[0];  //状态边界
    dp[0][1] = b[0];
    dp[0][2] = c[0];
    for (int i = 1; i < n; i++) {
        dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + a[i];
        dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + b[i];
        dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + c[i];
    }
    printf("%d", min(min(dp[n - 1][0], dp[n - 1][1]), dp[n - 1][2]));
    return 0;
}

五、编辑距离

在这里插入图片描述
这道题和最长公共子序列(LCS)类似,刚开始没有想出来,参照大佬的解析,放一个传送门:http://t.csdn.cn/2MWkR

备注

1.分治和动态规划最难的地方就是如何分解子问题,如何用状态表示当前问题,以及如何通过状态转移构造出完整的dp数组,这就得靠刷题找经验了······
2.动态规划等价于有向无环图,有起始节点和终止节点,本质是有限状态自动机,任何一个非起始节点都可以从其他节点转移过来;
3.注意如何通过dp表构造满足最优解的具体方案

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
动态规划是一种算法设计技术,通常用于解决最优化问题,它通过将大问题分解成相互重叠的小子问题,并存储每个子问题的解,避免了不必要的重复计算,从而提高了解题效率。以下是关于动态规划的一个1000字左右的笔记概要: **一、动态规划的基本思想** 1. **自底向上(Bottom-Up)**: 从最小规模的问题开始递推,逐层构建解决方案,最终得到最大规模问题的最优解。 2. **最优子结构(Optimal Substructure)**: 问题的最优解可以由其子问题的最优解组合而成。 **二、基本步骤** 1. **划分状态空间**: 确定问题的状态变量和状态转移方程。 2. **定义边界条件**: 定义基础情况,即最小规模问题的直接解。 3. **填充表格**: 根据状态转移方程,逐步填充表单,存储已计算结果。 4. **回溯求解**: 最终状态的结果就是原问题的解答。 **三、经典应用举例** 1. **斐波那契数列**: F(n) = F(n-1) + F(n-2),动态规划能高效地找出第n项。 2. **背包问题**: 求解物品价值最大化的问题,考虑物品选择的不同组合。 **四、注意事项** 1. 避免重复计算:利用数组或二维矩阵记录已计算结果。 2. 子问题间的关系:确保子问题不是独立的,而是有依赖关系。 3. 更新策略:选择合适的数据结构和更新规则。 **五、复杂度分析** - 时间复杂度:取决于问题的大小和子问题的重复程度。理论上,如果子问题数量为n,则时间复杂度可能是O(n^2)、O(n^3)等。 - 空间复杂度:需要存储中间结果,空间复杂度通常是O(n)或O(n^2)。 **六、实际案例分析** 详细讲解几个实际问题如何运用动态规划解决,例如最长公共子序列、最短路径等。 **七、总结及实践建议** - 动态规划适用于具有重叠子问题和最优子结构的问题。 - 通过实例理解并掌握分治法和动态规划的区别。 - 练习编写伪代码,熟悉动态规划的思维过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瓦耶_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值