【题目描述】
棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 AA 点能够到达 BB 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
【输入】
一行四个正整数,分别表示 B 点坐标和马的坐标。
【输出】
一个整数,表示所有的路径条数。
样例输入
6 6 3 3
样例输出
6
解题思路
首先想到的是搜索,用的是深度搜索,深搜代码如下:
#include<stdio.h>
long long book[25][25],n,m,q,p,max;
void dfs(int x0,int y0)
{
if(x0==n&&y0==m)//如果到达指定位置就把方案数加 1
{
max++;
return ;
}
long long next[3][2]={0,1,1,0};//定义卒的向右走,向下走两个方向
long long i;
for(i=0;i<2;i++)
{
int x,y;
x=next[i][1]+x0;
y=next[i][0]+y0;
if(x<0||y<0||x>n||y>m)//排除越界情况
continue;
if(book[x][y]==0)//当此处不是障碍物时,可以接着搜索
{
book[x][y]=1;
dfs(x,y);
book[x][y]=0;//回溯时要把标记去除
}
}
return ;
}
int main()
{
long long i;
//定义马的方向
long long next2[8][2]={2,1,1,2,-2,1,-1,2,-2,-1,-1,-2,2,-1,1,-2};
scanf("%lld %lld %lld %lld",&n,&m,&q,&p);
book[q][p]=1;
for(i=0;i<8;i++)//马的位置和马能走的位置都视为障碍物,用book数组标记为 1
{
int x,y;
x=q+next2[i][0];
y=p+next2[i][1];
if(x<0||y<0||x>n||y>m)//数组越界的情况就不能赋值
continue;
book[x][y]=1;
}
book[0][0]=1;//将起点赋值为 1,养成习惯,即使这个题不加上这个也没有问题
dfs(0,0);//起点(0,0)处开始搜索
printf("%lld",max);
return 0;
}
很遗憾是错的,时间超限了,但是深搜没有什么能改的地方,所以我试了下广度搜索,这个也是不行的。
只能用其他的方法了,仔细观察题目可以发现一个规律,毕竟这个 “ 卒 ” 只能往下走和往右走,那就说明:如果要求走到一个位置(x,y)有多少种方法,我就可以先求出这个位置(x,y)的上面那个位置(x-1,y)的方案数,再求出这个位置(x,y)的左边那个位置(x,y-1)的方案数,二者相加就表示走到(x,y)位置的方案数了。这个方法可以试试。
公式为:dp[i][j]=dp[i-1][j]+dp[i][j-1]
这里可以用一个二维数组来存储每个位置的方案数,要注意避免越界的情况,还有初始化,把 dp 数组为全局变量,然后要注意从第一个点开始,方案数一定要是 1,不然无论如何到了终点求出来的方案数都为 0,那么怎么做?
这个地方有些需要理解的东西,比如是如果这个二维数组从(0,0)开始,那么第 0 行的和第 0 列的方案数都有下标为 -1(不理解可以对照上面的状态方程)。所以这个 dp 数组一定不能从(0,0)开始,既然都要把二维数组整体移动了,那么起点,终点( n,m),马的坐标(q,p)都要更新。
在设置障碍物时,马移动的方向可以为(1,-2),这样还是需要在标记 book 数组时,要加另外一个步骤,因为数组是不能越界的,那么结合之前说的,既然要移动,还有保证马不能出界,直接把二维 dp 数组想右下方移两个单位就好了。
这里还有一个需要主意的点,因为数据范围较大,一般情况下,用 long long 类型定义计数器不容易错一些,比如这个题,用 int 过不了。是因为在卒移动过程中可以有些位置的方案数大于 int 的存储范围,即使最后的答案没有超过 int 的存储范围,还是不能 AC。(这里我还犯了一个错误,就是用了 long long 后居然忘了写 %lld)
代码如下:
#include<stdio.h>
long long dp[100][100],book[100][100];
int main()
{
long long n,m,q,p,i,j;
long long sum=0;//计数器
long long next[8][2]={2,1,1,2,-2,1,-1,2,-2,-1,-1,-2,2,-1,1,-2};//马的八个移动方向
scanf("%lld %lld %lld %lld",&n,&m,&q,&p);
n=n+2;
m=m+2;
q=q+2;
p=p+2;//将二维数组向右下方移动,并且起点终点,马的坐标都要移动
book[q][p]=1;//额外将马的坐标标记为 1
for(i=0;i<8;i++) //将马能走的地方标记为 1
{
int tx,ty;
tx=q+next[i][0];
ty=p+next[i][1];//此时不需要判断是否越界,想一想为什么,前面讲了
book[tx][ty]=1;
}
dp[2][1]=1;//这里是为了将起点的方案数设置为 1的,也可以写成 dp[1][2]=1
for(i=2;i<=n;i++)
{
for(j=2;j<=m;j++)
{
if(book[i][j]==1)//该点不能为障碍物
continue;
dp[i][j]=dp[i][j-1]+dp[i-1][j];//状态方程
}
}
printf("%lld",dp[n][m]);//将 dp数组下标为终点的方案数输出
return 0;
}