题目描述
小信又在迷宫中迷路了,这是一个 N × M N \times M N×M 的迷宫。迷宫里有一些障碍物,用 #
表示,能正常通过的格子用 .
表示。小信现在位于迷宫左上角 ( 1 , 1 ) (1, 1) (1,1) 的位置。
由于小信拥有超人的魔力,当他在位置 ( x , y ) (x, y) (x,y) 时,可以执行以下操作:
- 向右移动任意个单位(不能穿过障碍物),不能移出迷宫,即移动到位置 ( x , y + k ) { 1 ≤ k , y + k ≤ M } (x, y + k)\{1 \le k, y + k \le M\} (x,y+k){1≤k,y+k≤M}。
- 向下移动任意个单位(不能穿过障碍物),不能移出迷宫,即移动到位置 ( x + k , y ) { 1 ≤ k , x + k ≤ N } (x + k, y)\{1 \le k, x + k \le N\} (x+k,y){1≤k,x+k≤N}。
- 沿着当前位置的对角线向右下移动任意个单位(不能穿过障碍物),但必须在迷宫范围内,即移动到位置 ( x + k , y + k ) { 1 ≤ k ≤ min ( n , m ) , 1 ≤ x + k ≤ N , 1 ≤ y + k ≤ M } (x + k, y + k)\{1 \le k \le \min(n, m), 1 \le x + k \le N, 1 \le y + k \le M\} (x+k,y+k){1≤k≤min(n,m),1≤x+k≤N,1≤y+k≤M}。
现在他想知道从他当前的位置 ( 1 , 1 ) (1, 1) (1,1) 走到右下角 ( N , M ) (N, M) (N,M)有多少种方案数,答案对 998244353 998244353 998244353 取模。
输入格式
第一行输入两个正整数 N N N 和 M M M,表示迷宫的行数和列数。
接下来 N N N 行,每行 M M M 个字符数,表示有无障碍物。
输出格式
输出一个正整数,表示总共有多少方案数。
样例
样例输入1:
3 3
...
.#.
...
样例输出1:
10
样例解释1:
对于样例1,有如下10种方法:
- ( 1 , 1 ) → ( 1 , 2 ) → ( 1 , 3 ) → ( 2 , 3 ) → ( 3 , 3 ) (1, 1) \to (1, 2) \to (1, 3) \to (2, 3) \to (3, 3) (1,1)→(1,2)→(1,3)→(2,3)→(3,3)
- ( 1 , 1 ) → ( 1 , 2 ) → ( 1 , 3 ) → ( 3 , 3 ) (1, 1) \to (1, 2) \to (1, 3) \to (3, 3) (1,1)→(1,2)→(1,3)→(3,3)
- ( 1 , 1 ) → ( 1 , 2 ) → ( 2 , 3 ) → ( 3 , 3 ) (1, 1) \to (1, 2) \to (2, 3) \to (3, 3) (1,1)→(1,2)→(2,3)→(3,3)
- ( 1 , 1 ) → ( 1 , 3 ) → ( 2 , 3 ) → ( 3 , 3 ) (1, 1) \to (1, 3) \to (2, 3) \to (3, 3) (1,1)→(1,3)→(2,3)→(3,3)
- ( 1 , 1 ) → ( 1 , 3 ) → ( 3 , 3 ) (1, 1) \to (1, 3) \to (3, 3) (1,1)→(1,3)→(3,3)
- ( 1 , 1 ) → ( 2 , 1 ) → ( 3 , 1 ) → ( 3 , 2 ) → ( 3 , 3 ) (1, 1) \to (2, 1) \to (3, 1) \to (3, 2) \to (3, 3) (1,1)→(2,1)→(3,1)→(3,2)→(3,3)
- ( 1 , 1 ) → ( 2 , 1 ) → ( 3 , 1 ) → ( 3 , 3 ) (1, 1) \to (2, 1) \to (3, 1) \to (3, 3) (1,1)→(2,1)→(3,1)→(3,3)
- ( 1 , 1 ) → ( 2 , 1 ) → ( 3 , 2 ) → ( 3 , 3 ) (1, 1) \to (2, 1) \to(3, 2) \to (3, 3) (1,1)→(2,1)→(3,2)→(3,3)
- ( 1 , 1 ) → ( 3 , 1 ) → ( 3 , 2 ) → ( 3 , 3 ) (1, 1) \to (3, 1) \to (3, 2) \to(3, 3) (1,1)→(3,1)→(3,2)→(3,3)
- ( 1 , 1 ) → ( 3 , 1 ) → ( 3 , 3 (1, 1) \to (3, 1) \to (3, 3 (1,1)→(3,1)→(3,3)
样例输入2:
4 4
...#
....
..#.
....
样例输出2:
84
样例输入3:
8 10
..........
..........
..........
..........
..........
..........
..........
..........
样例输出3:
13701937
数据范围
对于 10 % 10\% 10% 的数据, 2 ≤ N , M ≤ 4 2 \le N, M \le 4 2≤N,M≤4。
对于 20 % 20\% 20% 的数据, 2 ≤ N , M ≤ 10 2 \le N, M \le 10 2≤N,M≤10。
对于 50 % 50\% 50% 的数据, 2 ≤ N , M ≤ 100 2 \le N, M \le 100 2≤N,M≤100。
对于 100 % 100\% 100% 的数据, 2 ≤ N , M ≤ 2000 2 \le N, M \le 2000 2≤N,M≤2000。
数据保证起点 ( 1 , 1 ) (1, 1) (1,1) 和终点 ( N , M ) (N, M) (N,M) 是 .
。
题解
1
考虑使用 dp
算法。
设 d p i , j dp_{i, j} dpi,j 表示到 ( i , j ) (i, j) (i,j) 的位置的方案数。
状态转移:
- 如果 a i , j a_{i, j} ai,j 等于
#
说明有障碍物,不能走, d p i , j = 0 dp_{i, j} = 0 dpi,j=0。
- 如果 a i , j a_{i, j} ai,j 等于
.
- 向下转移: ∑ x ≤ k < i ∀ k a k , j = . d p k , j ( 1 ≤ x ) \sum_{x \le k < i}^{\forall{k}\ a_{k, j} = .}dp_{k,j}(1 \le x) x≤k<i∑∀k ak,j=.dpk,j(1≤x)
即从 i i i 开始向前,一直到 a k , j a_{k, j} ak,j 是.
为止,并把 d p k , j dp_{k, j} dpk,j 加在 d p i , j dp_{i, j} dpi,j 里面。
cppfor(int k = i; k >= 1; -- k){ if(a[k][j] == '#') break; dp[i][j] += dp[k][j]; }
复杂度 O ( i ) \Omicron(i) O(i),近似 Θ ( N ) \Theta(N) Θ(N)。
2. 向右转移: ∑ x ≤ k < j ∀ k a i , k = . d p i , k ( 1 ≤ x ) \sum_{x \le k < j}^{\forall{k}\ a_{i, k} = .}dp_{i, k}(1 \le x) x≤k<j∑∀k ai,k=.dpi,k(1≤x)
即从 j j j 开始向前,一直到 a i , k a_{i, k} ai,k 是.
为止,并把 d p i , k dp_{i, k} dpi,k 加在 d p i , j dp_{i, j} dpi,j 里面。
cppfor(int k = j; k >= 1; -- k){ if(a[i][k] == '#') break; dp[i][j] += dp[i][k]; }
复杂度 O ( j ) \Omicron(j) O(j),近似 Θ ( M ) \Theta(M) Θ(M)。
3. 向右下转移: ∑ x ≤ k < min ( i , j ) ∀ a i − k , j − k = . ( 1 ≤ x ) \sum_{x \le k < \min(i, j)}^{\forall\ a_{i - k, j - k} = .}(1 \le x) x≤k<min(i,j)∑∀ ai−k,j−k=.(1≤x)
即从 i , j i, j i,j 开始向前,一直到 a i − k , j − k a_{i - k, j - k} ai−k,j−k 是.
为止,并把 d p i − k , j − k dp_{i - k, j - k} dpi−k,j−k 加在 d p i , j dp_{i, j} dpi,j 里面。
cppfor(int k = 1; k < min(j, k); ++ k){ if(a[i - k][j - k] == '#') break; dp[i][j] += dp[i - k][j - k]; }
复杂度 O ( min ( i , j ) ) \Omicron(\min(i, j)) O(min(i,j)),,近似 Θ ( min ( N , M ) ) \Theta(\min(N, M)) Θ(min(N,M))。
综上,我们就得出了 d p i , j dp_{i, j} dpi,j 的转移方程,时间复杂度近似 Θ ( 3 × K ) ( K = max ( N , M ) ) \Theta(3 \times K)(K = \max(N, M)) Θ(3×K)(K=max(N,M))。
答案就是走到 ( N , M ) (N, M) (N,M) 的方案数,即 d p N , M dp_{N, M} dpN,M。
还要加上取模。
复杂度为 Θ ( N × M × K ) ( K = max ( N , M ) ) \Theta(N \times M \times K)(K = \max(N, M)) Θ(N×M×K)(K=max(N,M)),会超时,能得 60 % 60\% 60% 的分数。
2
由于累加的值都是在一条直线上连续的值,考虑使用前缀和进行优化。
用 d p 0 / 1 / 2 , i , j dp_{0/1/2, i, j} dp0/1/2,i,j 分别表示横,纵,斜三个方向上到 ( i , j ) (i, j) (i,j) 的转移, d p 3 , i , j dp_{3, i, j} dp3,i,j 表示到 ( i , j ) (i, j) (i,j) 的方案数。
以横向为例。
如果 ( i , j ) (i, j) (i,j) 是 #
,则清空前缀和( d p 0 , i , j ← 0 dp_{0, i, j} \gets 0 dp0,i,j←0)。
如果 ( i , j ) (i, j) (i,j) 是 .
,则加上 d p 3 , i − 1 , j dp_{3, i - 1, j} dp3,i−1,j。(它既可以从前面转移,也可以从 ( i − 1 , j ) (i - 1, j) (i−1,j) 转移)。
复杂度为 Θ ( N × M ) \Theta(N \times M) Θ(N×M)。
cpp
输入
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= n; ++ j){
if(i == 1 && j == 1){
//初始化,最开始在 (1, 1),方案为 1
dp[3][i][j] = 1;
continue;
}
if(a[i][j] == '#'){
dp[0][i][j] = dp[1][i][j] = dp[2][i][j] = dp[3][i][j] = 0;
continue;
}
//横
dp[1][i][j] = (dp[1][i - 1][j] + dp[0][i - 1][j]) % mod;
//纵
dp[2][i][j] = (dp[2][i][j - 1] + dp[0][i][j - 1]) % mod;
//斜
dp[3][i][j] = (dp[3][i - 1][j - 1] + dp[0][i - 1][j - 1]) % mod;
//总方案数
dp[0][i][j] = ((dp[1][i][j] + dp[2][i][j]) % mod + dp[3][i][j]) % mod;
}
}
输出 dp[0][n][m]