动态规划-“三步走”方法
“三步走”方法
动态规划问题种类较多,但大多都能通过“三步走”方法解决。
1.状态表示:将具体问题抽象为数学问题,dp数组通常表示一个函数,分别分析其自变量和因变量的含义。
- 因变量:即dp数组的值表示的含义,大多数情况是题目是问什么就设什么,少部分情况是需要将问题表示转化一下。
- 自变量:即dp数组的每一维分别表示哪种状态,可根据数据规模和算法时间复杂度大致确定。
2.状态转移:状态转移相当于递推公式。主要有两种方式,可以从上一个状态转移到当前状态,或者从当前状态转移到下一个状态。可根据具体问题确定使用哪一种方式更方便。
3.边界条件:需要确定状态转移的初始值以及循环的边界。
4.部分细节:最后需要清楚答案存储的变量是哪一个,以及题目要求中一些细节。
举例说明:旅行2024
下面以一个线性dp的题目说明“三步走”方法如何使用。
题目描述
你面前有一张地图,其形状为一个由2行n列组成的网格。地图上的每一段道路都可能位于阳光下(白色)或阴影中(黑色)。
两个部分被认为是相连的,如果它们直接相邻并处于相同的状态(要么都是在阳光下,要么都是在阴影中)。一个图案被定义为一组具有相同状态的相连部分。
你的任务寻找有多少为地图的着色方案使得其恰好有
输入描述
唯一的一行包含两个整数
输出描述
输出恰好经历
样例输入
3 4
样例输出
12
样例解释
其中一种符合条件的方案如图所示:
样例输入
1 2
样例输出
2
下面进行三步走分析:
- 状态表示:用
数组表示状态, 中 表示 列中的第 列, 表示哪一种状态(共有 种状态), 表示当前已经表示了 种图案中的 种。 - 状态转移:当前状态只与上一时刻状态有关,所以需要知道上一列状态和当前列状态进行状态转移。上一时刻状态转移到当前状态变化的方案数可能有三种,
+0
,+1
,+2
。下面第一列表示上一列状态,第二列表示当前列状态,共有 种状态。
(1) 先讨论最简单的+2
的情况,共有两种可能:
可用10 01 01 10 ((u ^ v == 3) && abs(u - v == 1))
判断。
(2) 然后讨论相对简单的+0
的情况:
可用00 11 00 11 00 10 01 11 00 00 11 11 10 00 11 01 else if((u ^ v == 0) || u == 1 || u == 2)
判断,此处能用后两个判断条件也是因为写了else if
而不是直接用if
。
(3) 最后else
直接得出相对较复杂的+1
的情况,无需判断。下面也列出了所有+1
的情况:01 10 00 01 10 11 01 10 01 00 11 10 - 边界条件:由于
由 转移而来,所以需要提前设置 时的方案数, 只需从 循环到 即可, 需要从 循环到 ,上一时刻和当前时刻都需循环 种状态。由于随着 的增大, 不会减小,所以最终的答案在 中,在计算时需要取模。
根据以上分析编写代码如下:
#include <bits/stdc++.h> #define int long long #define forn(i, a, b) for (int i = a; i < b; i++) #define fore(i, a, b) for (int i = a; i <= b; i++) #define rfon(i, a, b) for (int i = a; i > b; i--) #define rfoe(i, a, b) for (int i = a; i >= b; i--) #define vb v.begin() #define ve v.end() #define vc v.clear() #define vs (int)v.size() #define ss (int)s.size() #define sb s.back() #define rs(i) resize(i) #define ft first #define sd second #define P pair<int, int> #define inf 0x3f3f3f3f #define Ios ios::sync_with_stdio(false), cin.tie(0) using namespace std; const int mod = 1e9 + 7; int dp[1005][4][2005]; void solve() { int n, k; cin >> n >> k; dp[1][0][1] = dp[1][1][2] = dp[1][2][2] = dp[1][3][1] = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j <= k; j++) { for (int u = 0; u < 4; u++) { for (int v = 0; v < 4; v++) { if ((u ^ v == 3) && abs(u - v == 1)) { dp[i][v][j + 2] = (dp[i][v][j + 2] + dp[i - 1][u][j]) % mod; } else if ((u ^ v == 0) || u == 1 || u == 2) { dp[i][v][j] = (dp[i][v][j] + dp[i - 1][u][j]) % mod; } else { dp[i][v][j + 1] = (dp[i][v][j + 1] + dp[i - 1][u][j]) % mod; } } } } } int ans = 0; for (int i = 0; i < 4; i++) { ans = (ans + dp[n][i][k]) % mod; } cout << ans << "\n"; } signed main() { Ios; solve(); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!