信息学奥赛一本通 1314:【例3.6】过河卒(Noip2002) | 1921:【02NOIP普及组】过河卒 | 洛谷 P1002 [NOIP2002 普及组] 过河卒

【题目链接】

ybt 1314:【例3.6】过河卒(Noip2002)
ybt 1921:【02NOIP普及组】过河卒
洛谷 P1002 [NOIP2002 普及组] 过河卒

【题目考点】

1. 坐标型动态规划

【解题思路】

设状态数组dp,dp[i][j]表示从(0,0)到(i,j)的路径条数。
考虑卒到(i,j)位置的前一个位置可能是哪里,把可能的到(i,j)的前一个位置的路径数量加和,即为到(i,j)的路径数量。

  • 如果(i,j)是(0,0),到起始位置的路径数量为1,所以:dp[0][0]=1;
  • 如果这个位置是马的控制点,那么到这里的路径数量为0。
  • 除了上述情况,如果是在第0行,即i为0,那么到(i,j)的前一个位置只能是(i,j-1)。所以如果i为0:dp[i][j] = dp[i][j-1];
  • 除了上述情况,如果是在第0列,即j为0,那么到(i,j)的前一个位置只能是(i-1,j)。所以如果j为0:dp[i][j] = dp[i-1][j];
  • 除了上述情况,到(i,j)的前一个位置只能是它上面的位置(i-1,j)和左边的位置(i,j-1),所以:dp[i][j] = dp[i-1][j]+dp[i][j-1];

先将dp数组初始化为-1,把马的控制点位置的值设为0。(0,0)位置设为1,而后遍历二维数组dp,按照上述递推关系做递推。最后输出dp[n][m]
【注意】:最终结果超出了int可以表示的范围,dp数组必须设为long long类型。

【题解代码】

解法1:递推 用dp的值判断一个位置是否被马控制

dp[i][j]为0表示该位置被马控制,为-1表示该位置尚未赋值。

#include <bits/stdc++.h>
using namespace std;
int dir[8][2]={{-1,2},{-1,-2},{1,2},{1,-2},{-2,1},{-2,-1},{2,1},{2,-1}};//方向数组:马走日下一步到的位置和当前位置的坐标差值 
int main() 
{ 
	long long dp[25][25];//dp[i][j]:表示从(0,0)到(i,j)的路径条数。
	memset(dp, -1, sizeof(dp)); 
	int n, m, cx, cy;
	cin >> n >> m >> cx >> cy;
	dp[cx][cy] = 0;//设置马的控制点,值为0 
	for(int i = 0; i < 8; ++i)
	{
		int x = cx + dir[i][0], y = cy + dir[i][1];
		if(x >= 0 && x <= n && y >= 0 && y <= m)
			dp[x][y] = 0;
	}
	dp[0][0] = 1;
	for(int i = 0; i <= n; ++i)
		for(int j = 0; j <= m; ++j)
		{
			if(dp[i][j] == 0)//马控制的点不做计算 
                continue;
			if(i == 0 && j == 0)
                dp[i][j] = 1;
            else if(i == 0)
				dp[i][j] = dp[i][j-1];
			else if(j == 0)
				dp[i][j] = dp[i-1][j];
			else
				dp[i][j] = dp[i-1][j] + dp[i][j-1];
		}
	cout << dp[n][m];
	return 0;
}
解法2:递推 设vis数字表示某位置是否被马控制

vis[i][j]为真表示该位置被马控制

#include <bits/stdc++.h>
using namespace std;
int dir[8][2]={{-1,2},{-1,-2},{1,2},{1,-2},{-2,1},{-2,-1},{2,1},{2,-1}};//方向数组:马走日下一步到的位置和当前位置的坐标差值 
long long dp[25][25];//dp[i][j]:表示从(0,0)到(i,j)的路径条数。初值为0。 
bool vis[25][25];//vis[i][j]:表示(i,j)位置是否被马控制 	
int main() 
{ 
	int n, m, cx, cy;
	cin >> n >> m >> cx >> cy;
	vis[cx][cy] = true;//设置马的控制点
	for(int i = 0; i < 8; ++i)
	{
		int x = cx + dir[i][0], y = cy + dir[i][1];
		if(x >= 0 && x <= n && y >= 0 && y <= m)
			vis[x][y] = true;
	}
	dp[0][0] = 1;
	for(int i = 0; i <= n; ++i)
		for(int j = 0; j <= m; ++j)
		{
			if(vis[i][j])//马控制的点不做计算 
                continue;
			if(i == 0 && j == 0)
                dp[i][j] = 1;
            else if(i == 0)
				dp[i][j] = dp[i][j-1];
			else if(j == 0)
				dp[i][j] = dp[i-1][j];
			else
				dp[i][j] = dp[i-1][j] + dp[i][j-1];
		}
	cout << dp[n][m];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值