地宫取宝

地宫取宝

$X$ 国王有一个地宫宝库,是 $n \times m$ 个格子的矩阵,每个格子放一件宝贝,每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是 $k$ 件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 $k$ 件宝贝。

输入格式

第一行 $3$ 个整数,$n$,$m$,$k$,含义见题目描述。

接下来 $n$ 行,每行有 $m$ 个整数 $C_{i}$ 用来描述宝库矩阵每个格子的宝贝价值。

输出格式

输出一个整数,表示正好取 $k$ 个宝贝的行动方案数。

该数字可能很大,输出它对 $1000000007$ 取模的结果。

数据范围

$1 \leq n,m \leq 50$,
$1 \leq k \leq 12$,
$0 \leq C_{i} \leq 12$

输入样例1:

2 2 2
1 2
2 1

输出样例1:

2

输入样例2:

2 3 2
1 2 3
2 1 5

输出样例2:

14

 

解题思路

  可以发现这是一个求集合最大值的问题,可以用动态规划。题目的限制有,每次只能向下或向右走、一定要按照递增的顺序取物品、要恰好取$k$件物品。

  dp的维度可以根据这几个限制来确定。首先用$2$维来表示坐标。再用$1$维来表示取的最后一件物品的值,因为物品是按照递增的顺序取的,因此只需要知道最后取的物品大小就可以了。再开一维来表示取了多少件物品。所以dp的维度为$4$维,$f \left( i,j,k,c \right)$。

  大概可以知道一共需要$5$重循环,前$4$重循环来枚举$4$个维度,还需要一个循环来进行状态转移计算。大概是一个$50 \times 50 \times 12 \times 13 \times \left( 25 \right)$的计算量。

  AC代码如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int N = 60, M = 15, mod = 1e9 + 7;
 6 
 7 int graph[N][N], f[N][N][M][M];
 8 
 9 int main() {
10     int n, m, num;
11     scanf("%d %d %d", &n, &m, &num);
12     for (int i = 1; i <= n; i++) {
13         for (int j = 1; j <= m; j++) {
14             scanf("%d", &graph[i][j]);
15             graph[i][j]++;      // c的取值范围变为1~13,因为0用来表示当k为0时取的最后一件物品的价值为0
16         }
17     }
18     
19     // 初始化,(1, 1)取一件物品,最后一件物品的价值就是graph[i][j]; (1, 1)不取物品,对应最小的数0
20     f[1][1][1][graph[1][1]] = f[1][1][0][0] = 1;
21     for (int i = 1; i <= n; i++) {
22         for (int j = 1; j <= m; j++) {
23             for (int k = 0; k <= num; k++) {
24                 for (int u = 0; u < M; u++) {
25                     f[i][j][k][u] = (f[i][j][k][u] + f[i - 1][j][k][u]) % mod;
26                     f[i][j][k][u] = (f[i][j][k][u] + f[i][j - 1][k][u]) % mod;
27                     if (u == graph[i][j] && k) {    // 因为时取得情况,要求k>0,且满足u==graph[i][j]
28                         for (int c = 0; c < u; c++) {
29                             f[i][j][k][u] = (f[i][j][k][u] + f[i - 1][j][k - 1][c]) % mod;
30                             f[i][j][k][u] = (f[i][j][k][u] + f[i][j - 1][k - 1][c]) % mod;
31                         }
32                     }
33                 }
34             }
35         }
36     }
37     
38     int ret = 0;
39     for (int i = 1; i < M; i++) {
40         ret = (ret + f[n][m][num][i]) % mod;
41     }
42     printf("%d", ret);
43     
44     return 0;
45 }

 

参考资料

  AcWing 1212. 地宫取宝(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/638/

posted @ 2022-02-18 10:32  onlyblues  阅读(131)  评论(0编辑  收藏  举报
Web Analytics