本文最后更新于 2024年6月7日 下午

最近一直在做动态规划的题,也算有了一些自己的小感悟,暂时总结一下
首先看一道例题
题目链接
描述
X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。
输入
输入一行 3 个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)
接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值
输出
要求输出一个整数,表示正好取 k 个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。
1 2 3 4 5 6 7
| 输入样例1 2 2 2 1 2 2 1
输出样例1 2
|
1 2 3 4 5 6 7
| 输入样例2 2 3 2 1 2 3 2 1 5
输出样例2 14
|
思考
- 动态规划,我们知道这是一种记录 T 时刻状态,通过状态转移方程,推导出 T+1 时刻的状态的算法,是一种空间换时间的算法,状态的记录通常使用数组。
- 求解目标:解决动态规划的问题,首先我们需要明确求解的目标是什么?求解目标就是数组元素的值。上面这道例题中,求解的目标就是到达终点满足条件的方法数目。那么数组元素中记录的值就是方法数。
- 数组:其次我们需要解决的是数组的维数,这个问题我们可以通过观察变量的数目来确定。上面的例题中,影响结果的变量有:横 m、纵坐标 n、物品最大价值 mp、物品数目 k 四个,所以需要引入的是一个数组四维数组。
- 起点:既然是根据前项推出后项,那么如同递归需要有一个出口一样,动态规划需要至少有一个起点——也即数组的某些元素的值是可以在初始阶段确定的。
- 在上面的例题中,
(1,1,0,0)元素的值可以确定(到达终点(1,1)位置,一个元素都不拿,物品的最大价值为 0,明显只有一种方法)
- 同时
(1,1,1,goods[1][1])元素的值也是能确定(只拿一个物品,那么只能拿(1,1)这个位置的物品,拿了之后物品最大价值的就是(1,1)的价值)
- 状态转移方程:最后需要明确的就是状态转移方程,在上面的例题中,到达一个位置拿 k 个物品,有两种方式。
-
不拿当前的物品,那么
Num[i][j][k][mp]+=Num[i-1][j][k][mp]+Num[i][j-1][k][mp]
-
当前位置的物品价值大于已经拿到的最大价值的情况下,那么可以拿起当前物品。但请注意拿起后物品数加一,已经拿到的最大价值也改变。
Num[i][j][k+1][goods[i][j]]+=Num[i-1][j][k][mp]+Num[i][j-1][k][mp]
到这里这道题的总体思路也就解决了,除此外到达终点的物品最大价值可能有多种情况,只要将所有价值情况下的方法数求和即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #define ll long long #define mod 1000000007 #include <iostream> #include <cstring>
using namespace std;
int graph[51][51];
ll num[51][51][13][13];
int main() { int n, m, k; cin >> n >> m >> k;
memset(graph, 0, sizeof(graph)); memset(num, 0, sizeof(num));
int max_price = 0; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { cin >> graph[i][j]; graph[i][j]++; max_price = max_price < graph[i][j] ? graph[i][j] : max_price; } }
num[1][1][0][0] = 1; num[1][1][1][graph[1][1]] = 1;
for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { for (int l = 0; l <= k; ++l) { for (int m = 0; m <= max_price; ++m) { if (i != 1) { num[i][j][l][m] += num[i - 1][j][l][m]; num[i][j][l][m] %= mod; if (graph[i][j] > m) { num[i][j][l + 1][graph[i][j]] += num[i - 1][j][l][m]; num[i][j][l + 1][graph[i][j]] %= mod; } } if (j != 1) { num[i][j][l][m] += num[i][j - 1][l][m]; num[i][j][l][m] %= mod; if (graph[i][j] > m) { num[i][j][l + 1][graph[i][j]] += num[i][j - 1][l][m]; num[i][j][l + 1][graph[i][j]] %= mod; } } } } } } int ans = 0; for (int i = 0; i <= max_price; i++) { ans = (ans + num[n][m][k][i]) % mod; } cout << ans << endl; return 0; }
|