动态规划 ( D y n a m i c (Dynamic (Dynamic P r o g r a m m i n g ) Programming) Programming)定义
从定义入手DP
DP是一种用于解决复杂问题的算法设计方法
通过将问题分解为相互重叠的子问题
并存储子问题的解以避免重复计算,从而提高效率
通常适用于具有最优子结构和重叠子问题性质的问题
先讲核心部分
核心部分
- 定义状态
明确问题的状态表示,通常用一个或多个变量描述子问题的解 - 状态转移方程
建立子问题之间的关系式,描述如何从已知状态推导出新的状态 - 初始化边界条件
确定最小子问题的解,作为递归或迭代的起点
为什么需要用DP呢?
DP有这更为可观的运行时间,比大多数算法更灵活
只要能计算出他的子问题的分布规律就可以轻松驾驭
DP的分类
- 线性DP
常用的DP之一,通常涉及一维或二维的状态表示
状态转移方程通常沿着线性顺序进行
包括带不限于最长上升子序列 ( L I S ) (LIS) (LIS)、 01 01 01背包、完全背包、最大子数组和 - 区间DP
常用DP之一,区间动态规划通常涉及对区间或子问题的划分
状态转移方程基于区间的合并或分割
例如经典问题石子合并等 - 树形DP
树形动态规划通常用于处理树形结构的问题
状态转移方程基于树的子树或子节点 - 状压DP(状态压缩DP)
状态压缩动态规划通常用于状态空间较大的问题
通过位运算或压缩技术减少状态表示的空间 - 数位DP
数位动态规划通常用于处理数字的各位或数位上的问题
状态转移方程基于数位的取值 - 概率DP
概率动态规划通常用于涉及概率或期望值的问题
状态转移方程基于概率的加权和 - 多维DP
多维动态规划通常涉及多个维度的状态表示
状态转移方程基于多个维度的组合
例如二维背包问题等
动态规划的分类并非严格独立
许多问题可能同时涉及多种类型的动态规划技术
实际应用中,需要根据问题的特点选择合适的动态规划方法
DP的优化
除此之外还有动态规划对状态转移方程的改进或空间复杂度的优化
例如:
- 斜率优化
- 单调队列优化
- 滚动数组优化
关于DP题单
例题来自 洛谷 洛谷 洛谷
【动态规划
1
】动态规划的引入
【动态规划1】动态规划的引入
【动态规划1】动态规划的引入
【动态规划
2
】线性状态动态规划
【动态规划2】线性状态动态规划
【动态规划2】线性状态动态规划
【动态规划
3
】区间与环形动态规划
【动态规划3】区间与环形动态规划
【动态规划3】区间与环形动态规划
【动态规划
4
】树与图上的动态规划
【动态规划4】树与图上的动态规划
【动态规划4】树与图上的动态规划
【动态规划
5
】状态压缩动态规划
【动态规划5】状态压缩动态规划
【动态规划5】状态压缩动态规划
PS:因为本文内容太多了,所以这里先给出题单方便刷题,实在找不到模板题了,就来个简单的例题吧
例题
P1002 [NOIP 2002 普及组] 过河卒
题目描述
棋盘上 A A A 点有一个过河卒,需要走到目标 B B B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C C C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示, A A A 点 ( 0 , 0 ) (0, 0) (0,0)、 B B B 点 ( n , m ) (n, m) (n,m),同样马的位置坐标是需要给出的。

现在要求你计算出卒从 A A A 点能够到达 B B B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 B B B 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
输入输出样例 #1
输入 #1
6 6 3 3
输出 #1
6
说明/提示
对于 100 % 100 \% 100% 的数据, 1 ≤ n , m ≤ 20 1 \le n, m \le 20 1≤n,m≤20, 0 ≤ 0 \le 0≤ 马的坐标 ≤ 20 \le 20 ≤20。
【题目来源】
NOIP 2002 普及组第四题
分析
先看题,题目非常简洁,就是说给个地图,图上有卒和马,问卒要从 A A A点到达 B B B点,有几种路线
第一眼看过去
好像DFS(深度优先搜索,忘记发了改天写qwq)
但是实际上写下去
会发现动态规划更好用些
不废话了,具体思路如下
因为卒只能往下或往右走,所以考虑DP
审题!
题目要求的不是回合制,卒走一步马走一步
马的位置是不变的,由此
推出马可以吃到卒的地方
换句话讲,就把
P
1
P_1
P1~
P
8
P_8
P8的位置以及马的位置标记为
1
1
1,就是不让卒走到这里
思路
初始化设置数组dp和地图数组a
a用于判断走这一步是否会被吃
如果会被吃就跳过
dp数组记得初始化dp[1][1]为1;
建议将数组定义在main函数外自动清零
记得将坐标都+1,防止误触边界
接下来上代码
#include <iostream>
using namespace std;
#define int long long
int a[25][25],dp[25][25];//地图
int ma[8][2]={{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1},{-2,1},{-1,2}};
//马的行走
signed main(){
int xma,yma,n,m;
cin>>n>>m>>xma>>yma;
n++,m++,xma++,yma++;
a[xma][yma]=1;
for(int i=0;i<8;i++){
int x,y;
x=xma+ma[i][0];
y=yma+ma[i][1];
a[x][y]=1;
}//标记马会走的地方
dp[1][1]=1;
//初始化!!
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1&&j==1)
continue;
if(a[i][j]==0){
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
cout <<dp[n][m];
return 0;
}
~ 完结撒花 完结撒花 完结撒花 ~
附:这是DP的一个简述,具体例题参考上面5个题单
下一篇预告:你猜
2万+

被折叠的 条评论
为什么被折叠?



