NOIP2002普及组T2(过河卒)题解

题目信息

题目传送门

解题思路

法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;
} 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒟蒻一枚

谢谢鸭~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值