题目描述
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。
只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
算法思想一(记忆化搜索)
根据题目描述,要计算的是第 1 行第 1 列、走到第 n 行第 m 列的合法方案。那么可以如下求解:
- 不妨用
f[i][j]
记录从 ( i , j ) (i,j) (i,j)出发走到第 n n n 行第 m m m 列的合法方案数。由于只能向右或者向下走,因此: f [ i ] [ j ] = f [ i + 1 ] [ j ] + f [ i ] [ j + 1 ] f[i][j]=f[i + 1][j]+f[i][j+1] f[i][j]=f[i+1][j]+f[i][j+1] - 又因为不能走入行号和列数都是偶数的格子,所以当
i
和j
同时为偶数时, f [ i ] [ j ] = 0 f[i][j] = 0 f[i][j]=0。 - 对于
f[n][m]
来说,如果n
和m
同时为偶数时, f [ n ] [ m ] = 0 f[n][m]=0 f[n][m]=0;否则 f [ n ] [ m ] = 1 f[n][m]=1 f[n][m]=1。
代码实现一(记忆化搜索)
#include <iostream>
using namespace std;
int n, m;
//f[i][j]表示从(i,j)出发,到达(n,m)的合法方案数
int f[35][35];
//记忆化搜索,从(x,y)点出发到达(n,m)的合法方案数
int dfs(int x, int y)
{
//如果x、y不同时为偶数
if(x & 1 || y & 1)
{
//已经搜索过,直接返回结果
if(f[x][y]) return f[x][y];
//在合法情况下,递归求解
if(x < n) f[x][y] += dfs(x + 1, y);
if(y < m) f[x][y] += dfs(x, y + 1);
}
//如果 x, y 都是偶数,那么 f[x][y] 就没被处理过,必然为 0
//否则,返回计算后的值
return f[x][y];
}
int main()
{
cin >> n >> m;
//初始化f[n][m],如果n和m为偶数,则为0;否则为1
f[n][m] = n & 1 || m & 1;
cout << dfs(1, 1) << endl;
return 0;
}
算法思想二(动态规划)
状态表示
f[i][j]
表示从
(
1
,
1
)
(1,1)
(1,1)出发走到第
i
i
i 行第
j
j
j 列的合法方案数。
状态计算
- 由于只能向右或者向下走,因此: f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − 1 ] f[i][j] = f[i - 1][j] + f[i][j - 1] f[i][j]=f[i−1][j]+f[i][j−1]
- 又因为不能走入行号和列数都是偶数的格子,所以当
i
和j
同时为偶数时, f [ i ] [ j ] = 0 f[i][j] = 0 f[i][j]=0。
初始状态
- f [ 1 ] [ 1 ] = 1 f[1][1] = 1 f[1][1]=1
代码实现
#include <iostream>
using namespace std;
const int N = 50;
int n, m;
int f[N][N];
int main()
{
cin >> n >> m;
f[1][1] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++)
{
if(i == 1 && j == 1) continue;
if(i % 2 == 0 && j % 2 == 0)
{
f[i][j] = 0;
continue;
}
f[i][j] = f[i - 1][j] + f[i][j - 1];
}
cout << f[n][m] << endl;
return 0;
}