今天我们将在豆包MarsCode AI刷题平台上,完成《生成大网格的总数》算法问题, 整体使用动态规划进行解答, 选择状态迁移的角度有点特别
生成大网格的总数
解析
- 由于操作只能选择一个宽为1,高为N/2的子网格, 则每次修改必定针对一列
- 一列有且仅能操作一次, 否则要么通不过,要么不符合修改规则(所选的所有单元格为0)
- 针对每一列, 可以通过上一列的数据进行状态迁移
最初考虑到点[i][j]的可能情况, 但是这种方式会由相同的伟大网格- 那么就考虑到达当前列时, 最终标记为1的情况, 由于这种标记是连续的, 可以使用 [l,r] 标记范围内的节点被标记为1
- 使用[0,0]表示当前列所有点都能访问的情况
- 那么就有两种情况
- 若当前列不添加网格,则由下面转移方程:
dp[i][0][0] = (dp[i][0][0] + dp[i - 1][l][r]) % mod; - 若当前列添加网格,则由下面转移方程:
int newL = l == 1 ? 1 : j; //l==1 必定封堵最上面进入的入口 int newR = r == N ? N : j + step; //r=N 必定堵住下面的入口 dp[i][newL][newR] = (dp[i][newL][newR] + dp[i - 1][l][r]) % mod; - 若当前列不添加网格,则由下面转移方程:
- tips: 这个路径没有说是一定要向下向左. 即可以向上走, 导致一段时间内一直找不出问题
具体实现
public static int solution(int N) {
int mod = 1000000007;
//使用标记上一次被封堵的范围{l,r} 那么就是以非这个范围之内的这样方式进入的数量
int move = N / 2 + 1; //操作后的格子不能重叠. N,那么有可能 N/2 + 1种操作的可能
int step = N / 2 - 1;
//这里的空间能够进一步压缩>>>>>>>>
long[][][] dp = new long[N + 1][N + 1][N + 1]; //保存i处, 存在封闭范围[l,r]时通过的次数
dp[0][2][N] = 1; //表明最开始就是除了第一格能通过,其他都不能通过
for (int i = 1; i <= N; i++) {
//不操作
dp[i][0][0] = dp[i - 1][0][0];
for (int l = 1; l <= move; l++) {
for (int r = l + step; r <= N; r++) {
dp[i][0][0] = (dp[i][0][0] + dp[i - 1][l][r]) % mod;
}
}
//操作[l,l+step]
for (int j = 1; j <= move; j++) {//添加[j,j+step]网格
dp[i][j][j + step] = (dp[i][j][j + step] + dp[i - 1][0][0]) % mod;
for (int l = 1; l <= move; l++) {
for (int r = l + step; r <= N; r++) { //可以知道这个范围一定是大于等于N
if (dp[i - 1][l][r] == 0) {
continue;
}
int newL = l == 1 ? 1 : j; //l==1 必定封堵最上面进入的入口
int newR = r == N ? N : j + step; //r=N 必定堵住下面的入口
if (newL == 1 && newR == N) {
continue;
}
dp[i][newL][newR] = (dp[i][newL][newR] + dp[i - 1][l][r]) % mod;
}
}
}
}
long result = dp[N][0][0];
for (int l = 1; l < move; l++) { //这里可以不等于move,因为没意义
for (int r = l + step; r < N; r++) { //不能等于N,否则到不了A[N-1][N-1]
result = (result + dp[N][l][r]) % mod;
}
}
return (int) result;
}