A Mio visits ACGN Exhibition
题意
有一个 n ∗ m n*m n∗m 的 01 01 01 矩阵,起点在 ( 1 , 1 ) (1,1) (1,1) 终点在 ( n , m ) (n,m) (n,m) ,每次只能往右或者往左,问从起点走到终点,路过的网格点中 0 0 0 的个数大于 p p p, 1 1 1 的个数大于 q q q 的路径有多少条
solution
朴素 bfs 中存储状态太多,会 mle,因此考虑动态规划
设 d p [ i ] [ j ] [ k ] [ l ] dp[i][j][k][l] dp[i][j][k][l] 表示当从起点走到 ( i , j ) (i,j) (i,j) 时, 0 0 0 的个数为 k k k, 1 1 1 的个数为 l l l 的路径数量,则可以简单推到当这个结点往右走或者往下走的下一个状态情况。
四维数组肯定开不下,已知当前抵达 ( i , j ) (i,j) (i,j) ,则易得当前已经走过 i + j − 1 i+j-1 i+j−1 个结点,那么就可以通过知道 0 0 0 的数量知道 1 1 1 的数量,这样可以砍掉一维。
但是还是不够,考虑滚动数组优化
每次状态转移对于 ( 1 , 1 ) (1,1) (1,1) 转移到的状态为 ( 1 , 2 ) (1,2) (1,2) 和 ( 2 , 1 ) (2,1) (2,1) ,接下来是 ( 3 , 1 ) (3,1) (3,1) 、 ( 2 , 2 ) (2,2) (2,2) 、 ( 1 , 3 ) (1,3) (1,3) ,因此斜着遍历矩阵,每次更新完下个状态后,当前状态则可被替换,这样将第一维优化至2.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 507;
const int mod = 998244353;
int a[N][N];
const int M = 1e4 + 7;
int dp[2][N][500 + 500 + 10];
int n, m;
int p, q;
inline int read() {
int s = 0, f = 1;
char ch;
do {
ch = getchar();
if (ch == '-')
f = -1;
} while (ch < 48 || ch > 57);
while (ch >= 48 && ch <= 57) {
s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
}
return s * f;
}
signed main() {
n = read(), m = read(), p = read(), q = read();
rep(i, 1, n) rep(j, 1, m) a[i][j] = read();
int c0 = (a[1][1] == 0), c1 = (a[1][1] == 1);
dp[1][1][c0] = 1;
rep(_, 2, n + m)
// 斜着去遍历 _ = i + j
for (int i = 1; i <= n; ++i) {
int j = _ - i;
if (j > m || j <= 0)
continue;
int x = i & 1, y = x ^ 1;
rep(k, 0, n + m ) {
if(dp[x][j][k]==0) continue;
int d = k + (a[i + 1][j] == 0), r = k + (a[i][j + 1] == 0);
dp[x][j + 1][r] = (dp[x][j + 1][r] + dp[x][j][k]) % mod;
dp[y][j][d] = (dp[y][j][d] + dp[x][j][k]) % mod;
if(i!=n)
dp[x][j][k]=0;
}
}
long long ans = 0;
rep(i, p, 1e3 + 1) if (n + m - 1 - i >= q) ans = (ans+ dp[n & 1][m][i])%mod;
printf("%d\n", ans);
return 0;
}