群晖可不可以做网站用千牛怎么做免费推广引流
A Mio visits ACGN Exhibition
题意
有一个 n∗mn*mn∗m 的 010101 矩阵,起点在 (1,1)(1,1)(1,1) 终点在 (n,m)(n,m)(n,m) ,每次只能往右或者往左,问从起点走到终点,路过的网格点中 000 的个数大于 ppp,111 的个数大于 qqq 的路径有多少条
solution
朴素 bfs 中存储状态太多,会 mle,因此考虑动态规划
设 dp[i][j][k][l]dp[i][j][k][l]dp[i][j][k][l] 表示当从起点走到 (i,j)(i,j)(i,j) 时,000 的个数为 kkk,111 的个数为 lll 的路径数量,则可以简单推到当这个结点往右走或者往下走的下一个状态情况。
四维数组肯定开不下,已知当前抵达 (i,j)(i,j)(i,j) ,则易得当前已经走过 i+j−1i+j-1i+j−1 个结点,那么就可以通过知道 000 的数量知道 111 的数量,这样可以砍掉一维。
但是还是不够,考虑滚动数组优化
每次状态转移对于 (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 + jfor (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;
}