C++算法·动态规划

动态规划 ( D y n a m i c (Dynamic (Dynamic P r o g r a m m i n g ) Programming) Programming)定义

从定义入手DP
DP是一种用于解决复杂问题的算法设计方法
通过将问题分解为相互重叠的子问题
并存储子问题的解以避免重复计算,从而提高效率
通常适用于具有最优子结构和重叠子问题性质的问题
先讲核心部分

核心部分

  1. 定义状态
    明确问题的状态表示,通常用一个或多个变量描述子问题的解
  2. 状态转移方程
    建立子问题之间的关系式,描述如何从已知状态推导出新的状态
  3. 初始化边界条件
    确定最小子问题的解,作为递归或迭代的起点

为什么需要用DP呢?

DP有这更为可观的运行时间,比大多数算法更灵活
只要能计算出他的子问题的分布规律就可以轻松驾驭

DP的分类

  1. 线性DP
    常用的DP之一,通常涉及一维或二维的状态表示
    状态转移方程通常沿着线性顺序进行
    包括带不限于最长上升子序列 ( L I S ) (LIS) (LIS) 01 01 01背包、完全背包、最大子数组和
  2. 区间DP
    常用DP之一,区间动态规划通常涉及对区间或子问题的划分
    状态转移方程基于区间的合并或分割
    例如经典问题石子合并等
  3. 树形DP
    树形动态规划通常用于处理树形结构的问题
    状态转移方程基于树的子树或子节点
  4. 状压DP(状态压缩DP)
    状态压缩动态规划通常用于状态空间较大的问题
    通过位运算或压缩技术减少状态表示的空间
  5. 数位DP
    数位动态规划通常用于处理数字的各位或数位上的问题
    状态转移方程基于数位的取值
  6. 概率DP
    概率动态规划通常用于涉及概率或期望值的问题
    状态转移方程基于概率的加权和
  7. 多维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 1n,m20 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个题单
下一篇预告:你猜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值