P1002 [NOIP2002 普及组] 过河卒

题目链接:

从起点走到终点,最后一步一定是向右或向下走过来的,因此就可以列出状态转移方程。值得注意的是,对于横着和竖着的两条边界不可直接想当然地认为路径数一定等于 \(1\),因为在中途可能会有控制点的存在,因此还是要老老实实地列出状态转移方程。由于边界时只会从一个方向递推过来,但是初始时下标从 \(1\) 开始导致 \(i=1\)\(j=1\)\(i-1\)\(j-1\) 都是 \(0\),因此也可以直接照搬原本的状态转移方程。

#include <bits/stdc++.h>

using LL = long long;
const int N = 25;
int bx, by, mx, my;
LL f[N][N], s[N][N];

bool check(int x, int y) {//判断马的“控制点”是否合法
	if (x < 1 || x > bx || y < 1 || y > by) return false;
	return true;
}

int main()
{
	std::cin >> bx >> by >> mx >> my;
	bx += 1, by += 1, mx += 1, my += 1;//下标从1开始
	
	s[mx][my] = 1;
	if (check(mx + 2, my + 1)) s[mx + 2][my + 1] = 1;
	if (check(mx + 2, my - 1)) s[mx + 2][my - 1] = 1;
	if (check(mx + 1, my + 2)) s[mx + 1][my + 2] = 1;
	if (check(mx + 1, my - 2)) s[mx + 1][my - 2] = 1;
	if (check(mx - 2, my - 1)) s[mx - 2][my - 1] = 1;
	if (check(mx - 2, my + 1)) s[mx - 2][my + 1] = 1;
	if (check(mx - 1, my - 2)) s[mx - 1][my - 2] = 1;
	if (check(mx - 1, my + 2)) s[mx - 1][my + 2] = 1;
	//将所有合法的控制点标记为真
	
	f[1][1] = 1;//如果起点即终点,那么就无需走,这也算一条路径
	for (int i = 1; i <= bx; i++) {
		for (int j = 1; j <= by; j++) {
			if ((i != 1 || j != 1) && !s[i][j]) f[i][j] = f[i - 1][j] + f[i][j - 1];//如果该位置不是控制点,且横纵坐标至少有一个不是1,即不是起点,就可以从两个方向去递推
		}
	}	
	std::cout << f[bx][by];
	return 0;
}

更易理解的版本:

#include <bits/stdc++.h>

using LL = long long;
const int N = 25;
int bx, by, mx, my;
LL f[N][N], s[N][N];

bool check(int x, int y) {
	if (x < 1 || x > bx || y < 1 || y > by) return false;
	return true;
}

int main()
{
	std::cin >> bx >> by >> mx >> my;
	bx += 1, by += 1, mx += 1, my += 1;
	
	s[mx][my] = 1;
	if (check(mx + 2, my + 1)) s[mx + 2][my + 1] = 1;
	if (check(mx + 2, my - 1)) s[mx + 2][my - 1] = 1;
	if (check(mx + 1, my + 2)) s[mx + 1][my + 2] = 1;
	if (check(mx + 1, my - 2)) s[mx + 1][my - 2] = 1;
	if (check(mx - 2, my - 1)) s[mx - 2][my - 1] = 1;
	if (check(mx - 2, my + 1)) s[mx - 2][my + 1] = 1;
	if (check(mx - 1, my - 2)) s[mx - 1][my - 2] = 1;
	if (check(mx - 1, my + 2)) s[mx - 1][my + 2] = 1;
	
	for (int i = 1; i <= bx; i++) {
		for (int j = 1; j <= by; j++) {
			if (s[i][j]) f[i][j] = 0;//是控制点就置零
			else {
				if (i == 1 && j == 1) f[i][j] = 1;//否则的话是起点就置为1
				else f[i][j] = f[i - 1][j] + f[i][j - 1];//否则就递推
			}
		}
	}	
	std::cout << f[bx][by];
	return 0;
}
posted @ 2024-04-05 22:37  胖柚の工作室  阅读(51)  评论(0)    收藏  举报