「过河卒」题目

洛谷题目<过河卒>

题目描述
棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,A点 (0, 0)、B点 (n, m),同样马的位置坐标是需要给出的。

在这里插入图片描述

现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式
一行四个正整数,分别表示 B 点坐标和马的坐标。

输出格式
一个整数,表示所有的路径条数。

输入输出样例
输入
6 6 3 3
输出
6
说明/提示
对于 100 % 的数据,1<=n,m<=20,0<=马的坐标<=20.

分析一下
第一种想法,这道题刚入手的时候,想到的是通过查找,由于数据较小,可以将所有的点通过二维数组表示出来,能够通过的点就赋值为1,不能通过的点就赋值为0。但是仔细想过后,发现不能够解决问题,当面临着可以向下走,也可以向左走的时候,该如何选择?就算选择了其中一种路径,可是继续走下去后,假如到达目标点或者遇到了死胡同,不能继续往前走了,又该如何返回并尝试另一种路径呢?
以上问题让我当时否定了我的这种想法,也许以后能够有办法解决,但是目前这个问题我一点思路还都没有。
第二种想法,我想到由一个点在坐标轴中到达另一个点的路径有多少种的问题,我在高中曾经遇到过这种数学题,也就是排列组合C(n,m),这个想法给我提供了另一种思路,假如说从点(0,0)到达点(3,4)的话,路径一共有C(7,3)或C(7,4)种。也许能够通过这个办法解决这个问题。可以先计算出由点(0,0)到(n,m)的总路径数,然后减去那些经过了马的控制点的路径,这个计算可以通过由点(0,0)到马的控制点的路径数 乘以 该控制点到目标点的路径数,但是减去的过程中,还会减去经过两个马的控制点的路径数(那些重复的部分)。这样下来,计算量会非常的大,而且很容易就多减去了,这种方法可能在最后我抽丝剥茧后会得出来正确结果,但是这种复杂程度,太容易错误。

重点来啦
最后我在洛谷上看了大神的题解,了解到一种方法:假设到达点(i,j)的路径数为f[i][j]条,那么f[i][j]=f[i-1][j]+f[i][j-1]。这个是重点,那么就可以通过递推来计算出到达那个点的路径。
上代码

#include<iostream>
using namespace std;
//20-7-8改 下面被注释掉的部分是昨天7-7写的,可惜写了半天,还是没写出来,于是今天去落谷上看题解了,有了一些思路,再来。
int main()
{
	int n,m,x,y,i,j;
	long long f[25][25]={0};
	int g[25][25]={0};
	cin>>n>>m>>x>>y;
	//防止越界 
	if(x<=18 && y<=19)
		g[x+2][y+1]=1;
	if(x<=19 && y<=18)
		g[x+1][y+2]=1;
	if(x>=1 && y<=18)
		g[x-1][y+2]=1;
	if(x>=2 && y<=19)
		g[x-2][y+1]=1;
	if(x>=2 && y>=1)
		g[x-2][y-1]=1;
	if(x>=1 && y>=2)
		g[x-1][y-2]=1;
	if(x<=19 && y>=2)
		g[x+1][y-2]=1;
	if(x<=18 && y>=1)
		g[x+2][y-1]=1;
	g[x][y]=1 ;
	//递推实现
	for(i=0;i<=n;i++)
		for(j=0;j<=m;j++)
			if(!g[i][j])//不为马的控制点时 
			{
				if(i==0 && j==0)
					f[0][0]=1;//最初
				else if(i==0 && j>0)
					f[0][j]=f[0][j-1];
				else if(i>0 && j==0)
					f[i][0]=f[i-1][0];
				else 
					f[i][j]=f[i-1][j]+f[i][j-1];	
			 } 
		cout<<f[n][m]<<endl;
		return 0;
 } 
 //下面这部分是我第二种思路,可惜最后失败了。
//#include <iostream>
//using namespace std;
//int cou(int x,int y)
//{
//	if(x<0 || y<0)
//	{
//		return(0);
//	}
//	int i,j,sum1=1,sum2=1,n,sum;
//	n=x+y;
//	for(i=n,j=1;j<=x;i--,j++)
//	{
//		sum1=i*sum1;
//	}
//	for(i=1;i<=x;i++)
//	{
//		sum2=i*sum2;
//	}
//	sum=sum1/sum2;
//	return sum;
//}
//int main()
//{
//	int a[28];
//	int i,j,m,n,x,y,sum;
//	cin>>n>>m>>x>>y;
//	sum=cou(n,m);
//	a[0]=cou(x+2,y+1)*cou(n-x-2,m-y-1);
//	a[1]=cou(x+1,y+2)*cou(n-x-1,m-y-2);
//	a[2]=cou(x-1,y+2)*cou(n-x+1,m-y-2);
//	a[3]=cou(x-2,y+1)*cou(n-x+2,m-y-1);
//	a[4]=cou(x-2,y-1)*cou(n-x+2,m-y+1);
//	a[5]=cou(x-1,y-2)*cou(n-x+1,m-y+2);
//	a[6]=cou(x+1,y-2)*cou(n-x-1,m-y+2);
//	a[7]=cou(x+2,y-1)*cou(n-x-2,m-y+1);
//	for(i=0;i<8;i++)
//	{
//		sum=sum-a[i];
//	}
//	a[0]=cou(x-1,y+2)*cou(n-x-1,m-y-2);
//	a[1]=cou(x-2,y+1)*2*cou(n-x+1,m-y-2);
//	a[2]=cou(x-2,y-1)*cou(1,3)*cou(n-x+1,m-y-2);
//	a[3]=cou(x-1,y-2)*cou(n-x+1,m-y-2);
//	a[4]=cou(x-2,y+1)*cou(3,1)*cou(n-x-1,m-y-2);
//	a[5]=cou(x-2,y-1)*cou(3,3)*cou(n-x-1,m-y-2);
//	a[6]=cou(x-1,y-2)*cou(2,4)*cou(n-x-1,m-y-2);
//	a[7]=cou(x+1,y-2)*cou(n-x-1,m-y-2);
//	a[8]=cou(x-2,y+1)*cou(n-x-2,m-y-1);
//	a[9]=cou(x-2,y-1)*cou(4,2)*cou(n-x-2,m-y-1);
//	a[10]=cou(x-1,y-2)*cou(3,3)*cou(n-x-2,m-y-1);
//	a[11]=cou(x+1,y-2)*cou(1,3)*cou(n-x-2,m-y-1);
//	a[12]=cou(x+2,y-1)*cou(n-x-2,m-y-1);
//	a[13]=cou(x-2,y-1)*cou(n-x+2,m-y-1);
//	a[14]=cou(x-2,y-1)*cou(n-x-2,m-y+1);
//	a[15]=cou(x-1,y-2)*cou(n-x-1,m-y+2);
//	a[16]=cou(x+1,y-2)*cou(n-x-2,m-y+1);
//	a[17]=cou(x-2,y+1)*2*cou(n-x-1,m-y-2);
//	a[18]=cou(x-2,y-1)*cou(1,2)*cou(n-x-1,m-y-2);
//	a[19]=cou(x-1,y-2)*cou(n-x-1,m-y-2);
//	a[20]=cou(x-2,y-1)*2*cou(n-x+1,m-y-2);
//	a[21]=cou(x-1,y-2)*cou(n-x-1,m-y-2);
//	a[22]=cou(x-2,y-1)*cou(n-x-2,m-y-1);
//	a[23]=cou(x-1,y-2)*cou(n-x-2,m-y-1);
//	a[24]=cou(x-1,y-2)*cou(3,1)*cou(n-x-2,m-y-1);
//	a[25]=cou(x+1,y-2)*2*cou(n-x-2,m-y-1);
//	a[26]=cou(x-2,y-1)*cou(n-x-2,m-y-1);
//	a[27]=cou(x-1,y-2)*2*cou(n-x-2,m-y+1);
//	for(i=0;i<28;i++)
//	{
//		sum=sum+a[1];
//	}
//	cout<<sum<<endl;
//}

第一次写,: ) 有点乱,以后尽量改一下。

再来个大神题解链接吧

添加链接描述

<think>好的,我需要解决用户关于洛谷“过河”问题的疑问。首先,用户希望了解解题思路或算法实现。根据提供的引用内容,问题涉及动态规划,特别是递推关系。 首先,问题描述是过河从A(0,0)到B,只能向右向下走,同时避开马的控制。马的位置和其跳跃可达的是障碍。需要计算路径数量。 接下来,我需要整理引用中的信息。引用[3]提到棋盘和马的控制,引用[4]指出状态转移方程是dp[i][j] = dp[i-1][j] + dp[i][j-1],但要注意马的控制。引用[1]和[2]提到递归方法效率低,动态规划是正解。 我需要考虑边界条件,比如起被马控制时直接返回0。然后初始化dp数组,处理马的控制,标记这些位置不可达。对于动态规划遍历,每个如果没有被控制,就累加上面和左边的路径数。 另外,要注意数组越界的问题,比如当i或j为0时,只能从一个方向来。可能需要将坐标整体偏移,避免负数索引。例如,将起设为(0,0),但实际处理时可能需要从1开始,这样i-1和j-1不会越界。 最后,代码实现部分需要处理输入,标记马的位置及其控制,初始化dp数组,然后逐行计算。测试案例验证正确性,比如题目中的示例输入。 用户可能需要进一步的优化方法,比如空间压缩,或者处理大数时的溢出问题。但根据引用内容,当前方法已经足够解决题目。需要确保代码正确处理所有边界情况,比如马在边界时的控制判断。</think>### 洛谷 P1002 过河 解题思路与算法实现 #### 问题描述 棋盘上,从 $A(0,0)$ 走到 $B(n,m)$,只能向右向下移动。棋盘上有一个敌方马,其位置为 $(x,y)$,马的控制包括自身位置和一步可达的8个。求所有合法路径的数量(需避开马的控制)[^3]。 --- #### 算法思路 1. **动态规划(DP)** 使用二维数组 $dp[i][j]$ 表示从起 $(i,j)$ 的路径总数,状态转移方程为: $$dp[i][j] = dp[i-1][j] + dp[i][j-1]$$ 需排除马的控制[^4]。 2. **边界条件** - 若起 $A(0,0)$ 或终 $B(n,m)$ 被马控制,直接输出0。 - 初始化第一行和第一列时,若路径被马控制阻断,后续均不可达。 3. **标记马的控制** 通过预计算标记所有马的控制,遍历时跳过这些。 --- #### 代码实现(Python) ```python def count_paths(n, m, horse_x, horse_y): # 标记马的控制 blocked = set() directions = [(-1, -2), (-2, -1), (-1, 2), (-2, 1), (1, -2), (2, -1), (1, 2), (2, 1), (0, 0)] for dx, dy in directions: x = horse_x + dx y = horse_y + dy if 0 <= x <= n and 0 <= y <= m: blocked.add((x, y)) # 初始化DP数组 dp = [[0] * (m + 1) for _ in range(n + 1)] dp[0][0] = 0 if (0, 0) in blocked else 1 # 动态规划遍历 for i in range(n + 1): for j in range(m + 1): if (i, j) in blocked: continue if i == 0 and j == 0: continue top = dp[i-1][j] if i > 0 else 0 left = dp[i][j-1] if j > 0 else 0 dp[i][j] = top + left return dp[n][m] # 示例输入:B(6,6),马在(3,3) print(count_paths(6, 6, 3, 3)) # 输出应为6 ``` --- #### 关键说明 1. **坐标偏移处理** 实际代码中可将棋盘坐标整体偏移,避免负数索引(如将起设为 $(1,1)$)。 2. **空间优化** 可将二维DP数组压缩为一维数组,仅保留当前行状态,降低空间复杂度至 $O(m)$。 3. **大数问题** 路径数可能极大,需根据题目要求使用高精度计算或取模操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值