题目信息
解题思路
法1:dfs(60pts)
先算出所有“马的控制点”,然后用深度优先搜索尝试每一条不途径这些点的路径。
不熟悉dfs请点这里,这里和这里。
法2:dp(正解)
- 定义dpij为从起点走到(i, j)的总方案数。
- 每个点可以从左边或者上边走过来,即dpij = dpi-1j + dpij-1
- 马和马的控制点不能走,即这些地方的dp为0。
不熟悉DP的小伙伴点这里
代码实现
法1
#include <bits/stdc++.h>
using namespace std;
const int dir_zu[2][2] = {{0, 1}, {1, 0}}; // 卒的方向数组,只能向右和向下走
const int dir_ma[2][9] = {{0, -2, -1, 1, 2, 2, 1, -1, -2},
{0, 1, 2, 2, 1, -1, -2, -2, -1}}; // 马的方向数组
int n, m; // B点坐标
int x, y; // 马的坐标
int sum; // 总方案数
bool is_danger[30][30]; // (i, j)是否危险,false代表安全
inline bool judge_in(int x, int y) { // 判断点(x, y)是否在棋盘里
return x >= 0 && x <= n && y >= 0 && y <= m;
}
void init() { // 初始化
is_danger[x][y] = true; // 马现在的点肯定是马的控制点
for (int i = 0; i < 9; ++i) {
int pre_x = x + dir_ma[0][i]; // 目前点的x坐标
int pre_y = y + dir_ma[1][i];
if (judge_in(pre_x, pre_y)) { // 判断是否在棋盘里
is_danger[pre_x][pre_y] = true; // 标记为不安全
}
}
}
void dfs(int x, int y) {
// 递归终止条件:卒已到达B点(坐标相等)
if (x == n && y == m) {
++sum;
return ;
}
// 尝试每一种方向
for (int i = 0; i < 2; ++i) {
int nxt_x = x + dir_zu[i][0]; // 下一个点的x坐标
int nxt_y = y + dir_zu[i][1]; // 下一个点的y坐标
if (!judge_in(nxt_x, nxt_y) || is_danger[nxt_x][nxt_y]) { // 判断是否可以继续走
continue;
}
dfs(nxt_x, nxt_y); // 可以走就继续走
}
}
int main() {
cin >> n >> m;
cin >> x >> y;
init();
dfs(0, 0);
cout << sum << '\n';
return 0;
}
法2
#include <bits/stdc++.h>
using namespace std;
const int dir[2][9] = {0, -2, -1, 1, 2, 2, 1, -1, -2,
0, 1, 2, 2, 1, -1, -2, -2, -1};
long long dp[40][40];
// 判断(i, j)是否可行
bool a[40][40];
int main() {
int nx, ny, hx, hy;
cin >> nx >> ny >> hx >> hy;
// 防止越界
nx += 2;
ny += 2;
hx += 2;
hy += 2;
dp[2][1] = 1;
a[hx][hy] = true;
for (int i = 1; i <= 8; ++i) {
a[hx + dir[0][i]][hy + dir[1][i]] = true;
}
for (int i = 2; i <= nx; ++i) {
for (int j = 2; j <= ny; ++j) {
if (a[i][j]) {
continue;
}
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
cout << dp[nx][ny] << '\n';
return 0;
}