动态规划算法初步总结
动态规划(DP)=状态表示+状态计算
其中:
状态表示为:集合(前i-1个元素的有限集合构成的最优解)+属性(max or min)
状态计算为是否将第i个元素加入集合中,即通过判断加入或不加入是否更符合优化目标决定
步骤可总结为:
- 输入
- 为使循环顺利进行,给目标数组赋边界值。多为一维数组为第0位取零,二维数组第0行第0列取零,对角线取零等
- 目标数组赋初值, 可赋当前状态-1时的值
- 目标数组从1到n循环计算,判断是否将第i个元素加入集合中
- 输出数组最后一项
关键在于构造递推方程,正确理解状态概念可以降低构造递推方程的难度
目前DP问题代码大都没有成型的步骤和体系,对初学者并不友好,初学者可能陷入仅仅背过一道题的代码而不会处理其它问题的境地。按照以上步骤处理可以解决很多简单的DP问题,实现初步掌握这一类题。
例如:
①0-1背包问题
#include <iostream> using namespace std; const int N = 1e3+10; int main() { int n, v; cin >> n >> v; int value[N], volume[N]; int key[N][N]; for (int i = 1; i <= n; i++) scanf("%d%d", &volume[i], &value[i]); //输入部分 for (int i = 0; i <= n; i++) { key[i][0] = 0; key[0][i] = 0;//赋两位下标取0时的边界值 } for (int i = 1; i <= n; i++) { for (int j = 1; j <= v; j++) { key[i][j] = key[i - 1][j];//根据当前状态减一给目标数组赋初值 if (j - volume[i] >= 0) { int temp = key[i - 1][j - volume[i]] + value[i]; if (temp > key[i][j]) key[i][j] = temp; }//判断是否将第i个元素加入集合中 } } cout << key[n][v] << endl; return 0; }
②图像压缩问题
#include <iostream> #include <cmath> using namespace std; const int N = 1e3+10; int main() { int n; cin >> n; int p[N], b[N]; for (int i = 1; i <= n; i++) scanf("%d", &p[i]); for (int i = 1; i <= n; i++) b[i] = ceil(log(p[i] + 1) / log(2)); int key[N]; key[0] = 0;//边界值 for (int i = 1; i <= n; i++) key[i] = key[i - 1]+ b[i]+11; //根据当前状态减一给目标数组赋初值 for (int i = 1; i <= n; i++) { int bmax=0; for (int j =i-2; j >=1; j--) { if (b[j] > bmax) bmax = b[j]; int temp= key[j] + (i - j)*bmax+11; if (key[i] > temp) key[i] = temp; }//判断是否将第i个元素加入集合中 } cout << key[n]; return 0; }
③公共子序列
#include <iostream> using namespace std; const int N = 1e3+10; int main() { int n,m; cin >> n>>m; char a[N], b[N]; cin >> a + 1 >> b +1; int key[N][N]; for (int i =0; i <= n; i++) key[i][0] = 0; for (int i = 0; i <=m; i++) key[0][i] = 0;//边界取零 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { key[i][j] = max(key[i - 1][j],key[i][j-1]);//赋初值 if (a[i] == b[j]) key[i][j] =max(key[i-1][j-1]+1,key[i][j]); } } cout << key[n][m]; return 0; }
④矩阵连乘
#include <iostream>using namespace std; const int N = 1e2 + 10; int main() { int key[N][N],p[N]; int n; cin >> n; for (int i = 0; i <= n; i++) scanf("%d", &p[i]); for (int i = 0; i <= n; i++) key[i][i] = 0; //边界值 for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { key[i][j] = key[i][j-1]+p[i-1]*p[j-1]*p[j]; //赋间隔为一的初值 for (int k = i + 1; k <= j; k++) { int temp = key[i][k]+p[i-1]*p[k]*p[j]; if (temp < key[i][j]) key[i][j] = temp; } } } cout << key[1][n]; return 0; }
由以上例子可见,对于简单动态规划问题,牢记以上步骤有助于初学者初步入门。进一步提升还需要多多刷题思考总结。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现