【问题描述】
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的某一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。
【输入】
给出n、m和C点的坐标。
【输出】
从A点能够到达B点的路径的条数。
【输入样例】
8 6 0 4
【输出样例】
1617
【算法分析】
跳马是一道老的不能再老的题目,我想每位编程初学者都学过,可能是在学回溯或搜索等算法的时候,很多书上也有类似的题目,一些比赛中也出现过这一问题的变形(如Noip1997初中组的第三题)。有些同学一看到这条题目就去搜索,即使你编程调试全通过了,运行时你也会发现:当n、m=15就会超时。
其实,本题稍加分析就能发现,要到达棋盘上的一个点,只能从左边过来(我们称之为左点)或是从上面过来(我们称之为上点),所以根据加法原理,到达某一点的路径数目,就等于到达其相邻的上点和左点的路径数目之和,因此我们可以使用逐列(或逐行)递推的方式来求出从起点到终点的路径数目。障碍点(马的控制点)也完全适用,只要将到达该点的路径数目设置为0即可。
用dp[i][j]表示到达点(i,j)的路径数目,g[i][j]表示点(i,j)有无障碍,g[i][j]=0表示无障碍,g[i][j]=1表示有障碍。则,递推关系式如下:
dp[i][j] = dp[i-1][j] + dp[i][j-1] // i>0且j>0且g[i][j]=0
递推边界有4个:
dp[i][j] = 0 // g[i][j] = 1
dp[i][0] = dp[i-1][0] // i>0且g[i][0] = 0
dp[0][j] = dp[0][j-1] // j>0且g[0][j] = 0
dp[0][0] = 1
考虑到最大情况下:n=20,m=20,路径条数可能会超过2^31-1,所以要用高精度。
【参考程序】
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 50
int arr[maxn][maxn]; // 记录马可以到达的地方
long long int dp[maxn][maxn]; // dp[