洛谷——P1002 过河卒

【题目描述】

棋盘上 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;
}

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

明里灰

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值