题目相关
题目链接
AtCoder Beginner Contest 183 E 题,https://atcoder.jp/contests/abc183/tasks/abc183_e。
Problem Statement
We have a grid with H horizontal rows and W vertical columns of squares. Square (i,j), which is at the i-th row from the top and j-th column from the left, is wall if Sij is #
and road if Sij is .
.
There is a queen, the chess piece, at Square (1,1). In one move, it can move any number of squares to the right, downwards, or diagonally to the lower right to a road square without jumping over wall squares.
In how many ways can the queen travel from Square (1,1) to Square (H,W)? Find the count modulo (10^9+7).
Here, two ways to travel are considered different if and only if there exists i such that the position of the queen after the i-th move is different in those two ways.
Input
Input is given from Standard Input in the following format:
H W
S11 ... s1W
.
.
.
SH1 ... SHW
Output
Print the number of ways, modulo (10^9+7), in which the queen can travel from Square (1,1) to Square (H,W).
Samples1
Sample Input 1
3 3
...
.#.
...
Sample Output 1
10
Explaination
There are 10 ways to travel, as follows:
- (1,1)→(1,2)→(1,3)→(2,3)→(3,3)
- (1,1)→(1,2)→(1,3)→(3,3)
- (1,1)→(1,2)→(2,3)→(3,3)
- (1,1)→(1,3)→(2,3)→(3,3)
- (1,1)→(1,3)→(3,3)
- (1,1)→(2,1)→(3,1)→(3,2)→(3,3)
- (1,1)→(2,1)→(3,1)→(3,3)
- (1,1)→(2,1)→(3,2)→(3,3)
- (1,1)→(3,1)→(3,2)→(3,3)
- (1,1)→(3,1)→(3,3)
Samples2
Sample Input 2
4 4
...#
....
..#.
....
Sample Output 2
84
Explaination
From (1,1), the queen can move to (1,2), (1,3), (2,1), (2,2), (3,1), or (4,1).
One possible path to (4,4) is (1,1)→(3,1)→(3,2)→(4,3)→(4,4).
Samples3
Sample Input 3
8 10
..........
..........
..........
..........
..........
..........
..........
..........
Sample Output 3
13701937
Constraints
- 2≤H,W≤2000
- Sij is
#
or.
. - S11 and SHW are
.
.
题解报告
题目翻译
有一个水平 H 行,垂直 W 列的棋盘。坐标 (i, j) 表示从上开始数的第 i 行,从左开始数的第 j 列。如果 Sij 是墙用 # 表示,如果 Sij 是道路用 . 表示。
在坐标 (1, 1) 有一个皇后,每次移动可以向右,向下,从右下对角线移动任意位置,只要道路中没有墙壁。问从 (1, 1) 到 (H, W) 一共右多少种走法,答案对 10^9+7 取模。
题目分析
刚读到这个题目,第一反应使用 BFS 来遍历找出所有的可能。但是仔细考虑后,BFS 一般用来找最短路线,找出所有可能,其实是一个数学题。
根据题目提供的规则,我们可以绘制出皇后的走法
上图中,红色圆圈表示皇后,只要路线上没有墙,她可以向左、向下、右下这三个方向移动任意位置。所以本题如果使用 BFS 来遍历,还真不好写。
由于皇后可以走的方式有三种,我们可以定义三个数组来描述走法,比如用数组 row 表示水平方向走法,col 表示垂直方向走法,dia 表示斜对角线走法。那么 row[i] 表示第 i 行的走法,col[j] 表示第 j 列的走法,斜对角线的计算方法是 i-j,那么会出现负数,数组的下标是不允许出现负数,因此需要增加一个偏移量将负数变为整数,根据本题的数据,最大的偏差是 i=2, j=2000 的时候,也就是 -1998,因此我们可以定义偏移量为 2000。三个数组的定义如下:
const int MAXH=2e3+2;
const int MAXW=2e3+2;
char nums[MAXH][MAXW];//用于保存迷宫
long long row[MAXH];//行走法
long long col[MAXW];//列走法
long long dia[MAXH+MAXW];//对角线
const long long MO=1e9+7;
/*计算对角线的时候 i-j 可能出现负数,最大的负数是 1-2000=-1999,所以需要一个 OFFSET 保证正下标*/
const int OFFSET=max(MAXH, MAXW);//只要超过MAXH和MAXW最大值就可以
根据题目的要求可以知道,(i, j) 如果为 #,说明这里不能走,也就是没有走法,我们设置对应的 row、col、dia 为零。如下
if ('#'==nums[i][j]) {
row[i] = 0;
col[j] = 0;
dia[i-j+OFFSET] = 0;
}
(i, j) 如果为 .,说明这里可以走。我们使用样例 1 数据来分析一下,怎么写出走法的递推公式。其实这个递推公式非常容易写,根据走法规则,我们知道要走到 (i, j) 点,我们有三种走法左边向右,上面向下,左上向右下走,因此就是将这三个方向的数据加起来即可。
样例数据 1
根据样例数据 1,我们可以绘制出以下的地图。这里,我们取 OFFSET 为 2002。
坐标 (1, 1),可以走。i=1, j=1,row[1]=1,col[1]=1,dia[2002]=1。因为在起点,我们三个方向的走到 (1,1) 对应的走法都是 1。
坐标 (1, 2),可以走。i=1, j=2,row[1]=2,col[2]=1,dia[2001]=1。
坐标 (1, 3),可以走。i=1, j=3,row[1]=4,col[3]=2,dia[2000]=2。
坐标 (2, 1),可以走。i=2, j=1,row[2]=1,col[1]=2,dia[2003]=1。
坐标 (2, 2),不可以走。i=2, j=2,row[2]=0,col[2]=0,dia[2002]=0。
坐标 (2, 3),可以走。i=2, j=3,row[2]=3,col[3]=5,dia[2001]=4。
坐标 (3, 1),可以走。i=3, j=1,row[3]=2,col[1]=4,dia[2004]=2。
坐标 (3, 2),可以走。i=3, j=2,row[3]=5,col[2]=3,dia[2003]=4。
坐标 (3, 3),可以走。i=3, j=3,row[3]=15,col[3]=15,dia[2002]=10。
本题考点
递推。
AC 参考代码
//https://atcoder.jp/contests/abc183/tasks/abc183_e
//E - Queen on Grid
#include <bits/stdc++.h>
using namespace std;
const int MAXH=2e3+2;
const int MAXW=2e3+2;
char nums[MAXH][MAXW];//用于保存迷宫
long long row[MAXH];//行走法
long long col[MAXW];//列走法
long long dia[MAXH+MAXW];//对角线
const long long MO=1e9+7;
/*计算对角线的时候 i-j 可能出现负数,最大的负数是 1-2000=-1999,所以需要一个 OFFSET 保证正下标*/
const int OFFSET=max(MAXH, MAXW);//只要超过MAXH和MAXW最大值就可以
int main() {
#if 1
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#endif
int h,w;
cin>>h>>w;
for (int i=1; i<=h; i++) {
for (int j=1; j<=w; j++) {
cin>>nums[i][j];
}
}
long long ans=-1;
for (int i=1; i<=h; i++) {
for (int j=1; j<=w; j++) {
if ('#'==nums[i][j]) {
row[i] = 0;
col[j] = 0;
dia[i-j+OFFSET] = 0;
} else {
long long res = (row[i]+col[j]+dia[i-j+OFFSET])%MO;
if (1==i && 1==j) {
res++;
}
ans = res;
//更新
row[i] = (row[i]+res)%MO;
col[j] = (col[j]+res)%MO;
dia[i-j+OFFSET] = (dia[i-j+OFFSET]+res)%MO;
}
}
}
cout<<ans<<"\n";
return 0;
}
上图的两个时间分别是用流式快读和结构化读入。其中 84ms 是快读,64ms 是结构化。
时间复杂度
O(H*W),我们需要遍历所有数据。
空间复杂度
O(H*W),我们需要保存所有数据。