马拦过河卒(递推)dp图解

马拦过河卒
Description
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。棋盘用坐标表示,A点(0,0)、B点(n,m)(n,m为不超过15的整数),同样马的位置坐标是需要给出的。现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
Input
一行四个数据,用空格分隔,分别表示B点的坐标和马的坐标。
Output
一个数据,表示所有的路径条数。
Sample
Input
6 6 3 3
Output
6

AC代码直通车

我们用题目题目中的数据进行讲解分析
1)首先我们会得到如下坐标分布:A点(0,0) B点(6,6) 马(3,3)
又因为B点坐标,n,m为不超过15 的整数,所以需要16*16的数组(我这里只画了15*15),map这个数组来记录每个坐标点能否通行(赋值1代表可以通行,0代表控制点不可以通行)
还需要另外一个数组来记录可以到达当前坐标点的路线数tripnum,也是16*16;
在这里插入图片描述
2)我们对map数组全部赋值为1,代表初始全部可以通行

for(int i=0;i<16;i++)
	for(int j=0;j<16;j++)
		map[i][j]=1; 

3)然后对马的控制点赋值为0,代表不可通行。
这里涉及到一个马的控制点坐标的问题,可以用相对坐标来表示,玩过象棋的朋友都知道,“马走日”可以控制八个点,称作“八面威风”,也就是下面这八个点。
用两个数组来存他们与马坐标的相对位置坐标,相同下标组合成一组坐标,然后用马的坐标加上他们的相对坐标得到控制点的实际坐标

int a[9]={0,-1,-1,-2,-2,1,1,2,2};
int b[9]={0,2,-2,1,-1,2,-2,1,-1};
for(int i=0;i<9;i++)
{
	if(x+a[i]<=15 && x+a[i]>=0 && y+b[i]<=15 && y+b[i]>=0)	//判断是不是在map地图以内
		map[x+a[i]][y+b[i]]=0;
}

在这里插入图片描述
4)因为卒只能向下向右运动,所以map数组中的第一行和第一列的坐标,只能是通过(0,0)直接到达,所以如果它们可以通行,那么到达此坐标的路线也只会是一条(这里想一下)
所以这里我们遍历map数组是否可以通行,如果可以则在tripnum数组记录下可以到达此坐标的路线数,对于第一行和第一列来说,当存在一个控制点的时候,那么控制点后面的肯定是无法到达的,所以直接break。

for(int i=0;i<16;i++)
	{
		if(map[0][i]==1)
		{
			tripnum[0][i]=1;
		}
		else
			break;
	}
	for(int i=0;i<16;i++)
	{
		if(map[i][0]==1)
		{
			tripnum[i][0]=1;
		}
		else
			break;
	}

5)之前的都是铺垫,那么下面就是这个题的核心代码了
我们开始计算到达B点的所有路线。要想到达B点只能通过B点上方或左边的坐标,所以我们要计算到达一个点的路线总数的时候就要,把坐标点左边的路线数加上坐标点上边的路线数,这里就涉及到了递推的原理。
但是如果一个点是控制点的话,它也是可以到达的,具有到达路线数值,但是不可通行。所以进行计算可到达路线数时候,先判断一下是不是可通行点,如果是,则进行计算;如果不是,在保持初值0;
最后打印出tripnum[i][j]的值就可以了

for(int i=1;i<=n;i++)
{
	for(int j=1;j<=m;j++)
	{
		if(map[i][j]==1)
			tripnum[i][j]=tripnum[i-1][j]+tripnum[i][j-1];
	}
}

完整AC代码

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{	
	int a[9]={0,-1,-1,-2,-2,1,1,2,2};
	int b[9]={0,2,-2,1,-1,2,-2,1,-1};
	int map[16][16]={0};
	long long tripnum[16][16]={0};
	int n,m,x,y;
	scanf("%d %d %d %d",&n,&m,&x,&y);
	for(int i=0;i<16;i++)
		for(int j=0;j<16;j++)
			map[i][j]=1; 
	for(int i=0;i<9;i++)
	{
		if(x+a[i]<=15 && x+a[i]>=0 && y+b[i]<=15 && y+b[i]>=0)
		map[x+a[i]][y+b[i]]=0;
	}
	for(int i=0;i<16;i++)
	{
		if(map[0][i]==1)
		{
			tripnum[0][i]=1;
		}
		else
			break;
	}
	for(int i=0;i<16;i++)
	{
		if(map[i][0]==1)
		{
			tripnum[i][0]=1;
		}
		else
			break;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(map[i][j]==1)
			tripnum[i][j]=tripnum[i-1][j]+
							tripnum[i][j-1];
		}
	}
	printf("%lld",tripnum[n][m]);
	return 0;
} 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值