Description
从前有两个青蛙王国,两个王国商业都非常繁荣。但是一块池塘阻碍了两国的商业往来。一次,两只青蛙在池塘的两岸,他们都希望到对岸去。我们可以将池塘看做一个
n
×
m
n×m
n×m 的矩形,在每个格子里,可能会有荷叶。青蛙必须踩在荷叶上,不能跳进水里。如图青蛙可以向他前方的5个有荷叶的地方跳去。
由于有的地方荷叶比较小,当一个青蛙从该荷叶上跳走之后,荷叶会沉入水底,两个青蛙也不能同时跳上这种荷叶。两个青蛙想知道有多少种方式使他们都到达对岸。第一个青蛙可以从第一行任何一个有荷叶的格子出发。第二个青蛙可以从最后一行任何一个有荷叶的格子出发。当第一个青蛙到达最后一行任何一个有荷叶的格子时,他就算到达了对岸。当第二个青蛙到达第一行任何一个有荷叶的格子时,他也算到达了对岸。请你帮助青蛙们计算有多少种方案可以让他们都到达对岸。
注:第一个青蛙只能向下跳,第二个青蛙只能向上跳。青蛙并不能跳出矩形区域。
Input
输入的第一行包含两个整数
n
n
n ,
m
m
m。
第2至
n
+
1
n+1
n+1 行包含
m
m
m 个整数。若该数为0,表示该格子上没有荷叶,青蛙不能通过。若该数为1,表示该格子上的荷叶只允许一个青蛙通过。若该数为2,表示该格子上的荷叶可以允许两个青蛙都通过。
Output
输出的第一行包含一个整数,表示两个青蛙都到达对岸的方案数。由于结果可能非常大,输出答案模1000000007的结果。
Sample Input
Test Case #1
4 1
2
0
0
2
Test Case #2
5 3
1 0 1
0 0 0
0 1 0
0 0 0
1 0 1
Test Case #3
4 3
1 1 0
0 0 0
0 0 1
2 0 0
Sample Output
Test Case #1
1
Test Case #2
0
Test Case #3
2
Data
对于30%的数据:
n
,
m
≤
10
n , m \leq 10
n,m≤10
对于另外20%的数据,格子只有0或2两种
对于100%的数据,
n
,
m
≤
50
n , m \leq 50
n,m≤50
Associate
看样子这道题暴力的分数并不是很多。不如想想正解
但是非常抱歉,我考试的时候只剩下15min了。真是失误,我获得了0分的好成绩。因为根本就没怎么想,没时间了,样例都没测就交了
Solution
直接来说正解怎么做吧:
很简单能发现,这道题应该是一个动态规划题。我们考虑设计一个状态:
d
p
[
i
]
[
j
]
[
k
]
[
l
]
dp[i][j][k][l]
dp[i][j][k][l] 表示当第一支青蛙走到
(
i
,
j
)
(i , j)
(i,j) ,第二支青蛙走到
(
k
,
l
)
(k , l)
(k,l) 的时候的状态数目。
这里有一个常用的技巧:因为图是可以颠倒的,但是一个从上面走,一个从下面走,不方便计数,那么可以把图翻个180度,相当于两只青蛙都从一边出发,方案数必定是一样的
考虑这个dp方程。实际上还是挺好想的。但是其中可以添加一个剪枝,就是 k k k 一定要在 i − 3 i - 3 i−3 到 i + 3 i + 3 i+3 之间。因为这样的话,他们差的不会很远,否则可能会导致一只青蛙没地方走了的情况,只能原地不动。这是不能允许的。
剩下的就非常简单了。我们循环一下5种转移方式(就是青蛙的跳动方式),分别更新。
这里要注意,只更新当前行数更小的那只青蛙,让他往前面跳,永远都是慢的在追快的,否则会越差越多,最后超出了3的范围,就不能更新了。
在方程中不断累加,最后把都在最后一行的两只青蛙的方案数加起来就行了。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
inline int read() {
int x = 0 , f = 1; char ch = getchar();
for ( ; !isdigit(ch) ; ch = getchar()) if (ch == '-') f = -1;
for ( ; isdigit(ch) ; ch = getchar()) x = x * 10 + ch - '0';
return x * f;
}
const int maxn = 51;
const int mod = 1e9 + 7;
const int maxDir = 5;
const int dirx[maxDir] = {1 , 1 , 2 , 2 , 3};
const int diry[maxDir] = {-2 , 2 , -1 , 1 , 0};
int n , m;
int _map[maxn][maxn];
int dp[maxn][maxn][maxn][maxn];
int ans;
int main() {
n = read() , m = read();
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
_map[i][j] = read();
}
}
for (int i = 1 ; i <= m ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
if (i == j && _map[1][i] == 2) {//两只青蛙一起站在一个为2的荷叶上
dp[1][i][1][i] = 1;
}
else if (i != j && _map[1][i] && _map[1][j]) {//两只青蛙分别站在不是水的两片荷叶上
dp[1][i][1][j] = 1;
}
}
}
for (int i = 1 ; i <= n ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
for (int k = max(1 , i - 3) ; k <= min(n , i + 3) ; k ++) {
for (int l = 1 ; l <= m ; l ++) {
if (dp[i][j][k][l]) {
if (i >= k) {
for (int dir = 0 ; dir < maxDir ; dir ++) {
int newx = k + dirx[dir] , newy = l + diry[dir];
if (newx <= 0 || newx > n || newy <= 0 || newy > m) { //越界
continue;
}
if (!_map[newx][newy]) { //下面要去的点是水
continue;
}
if (newx == i && newy == j && _map[i][j] == 1) {//两只青蛙一起到了一个只能装1个青蛙的荷叶上
continue;
}
dp[i][j][newx][newy] = (dp[i][j][newx][newy] + dp[i][j][k][l]) % mod;//累加
}
}
else {//同理
for (int dir = 0 ; dir < maxDir ; dir ++) {
int newx = i + dirx[dir] , newy = j + diry[dir];
if (newx <= 0 || newx > n || newy <= 0 || newy > m) {
continue;
}
if (!_map[newx][newy]) {
continue;
}
if (newx == k && newy == l && _map[k][l] == 1) {
continue;
}
dp[newx][newy][k][l] = (dp[newx][newy][k][l] + dp[i][j][k][l]) % mod;
}
}
}
}
}
}
}
for (int i = 1 ; i <= m ; i ++) {
for (int j = 1 ; j <= m ; j ++) {
ans = (ans + dp[n][i][n][j]) % mod; // 第一只青蛙在 i ,第二只青蛙在 j 的方案数
}
}
printf("%d\n" , ans);
}