👨🏫 集合划分依据:找最后一个不同点
👨🏫 边界问题(出口 ):从实际问题出发
① 用到 i-1 ,j-1 ,下标从 1 开发,减少特判
🐷 地宫取宝
🧐 解题思路
① 两维存坐标 下 x y,一维存 最大值(不是最大值的所在坐标),再一维存当前已取个数
② 复杂度预估:x: 50,y:50,最大值:12,取得个数:13;
50*50*12*13 ≈4 * 10^5 -> 10^7 / 4*10^5 = 25
③ 四重for循环 + 状态计算 (刚刚好hh)
④ 集合划分:
⭐ 一分 从左往右走 和 从上往下走
⭐ 二分 取当前点 和 不取当前点
✨ 对于 取 的情况
✨ 三分 取当前数 之前的 最大值是什么
⑤ 结果: f [n][m][k][i], 1 <= i <= 13;
👨🏫 因为物品的值有可能是 0 ,所以 得用到 -1 初始化没取一个宝的情况,但是数组下标不能为 负数 ,
所以直接给所有价值+10 表示一件未取的情况
👨🏫 运算记得按题目要求取模,防止溢出
import java.util.*;
public class Main
{
static int N = 55, M = 15, MOD = (int) 1e9 + 7;
static int n, m, c;
static int[][] a = new int[N][N];
//状态数组,相当于坑位,从前往后枚举,找到符合条件的方案就填进坑位里边 存的值是当前状态的方案数
static int[][][][] f = new int[N][N][M][M];
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
c = sc.nextInt();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
a[i][j] = sc.nextInt();
a[i][j]++;
}
// 初始化边界
f[1][1][0][0] = 1;// 不选的情况,最大值 为 -1 + 1 = 0
f[1][1][1][a[1][1]] = 1;// 选 1
for (int i = 1; i <= n; i++)// i 是横坐标
for (int j = 1; j <= m; j++)// j 是纵坐标
for (int cnt = 0; cnt <= c; cnt++)// cnt 是已取数 个数
for (int max = 0; max < M; max++)// k 是 已取数中的最大的值
{
// 不取
// 没有加入新的宝,对于数量和最大值都是没有影响的
f[i][j][cnt][max] = (f[i][j][cnt][max] + f[i - 1][j][cnt][max]) % MOD;// 不使用 += 是为了方便取模
f[i][j][cnt][max] = (f[i][j][cnt][max] + f[i][j - 1][cnt][max]) % MOD;
// cnt > 0 表示前边有数了
if (cnt > 0 && max == a[i][j])// k == a[i][j] ,条件符合,可以填坑
{
// 可以拿的情况
// 枚举前边的方案(前一个最大值)
for (int s = 0; s < max; s++)
{
// s 是已取数 倒数第二位的 值
f[i][j][cnt][max] = (f[i][j][cnt][max] + f[i - 1][j][cnt - 1][s]) % MOD;
f[i][j][cnt][max] = (f[i][j][cnt][max] + f[i][j - 1][cnt - 1][s]) % MOD;
}
}
}
long res = 0;
for (int i = 0; i < M; i++)
res = (res + f[n][m][c][i]) % MOD;
System.out.println(res);
}
}