首页 > 其他分享 >P1002 过河卒 详细题解 搜索回溯+递归 [NOIP2002 普及组]

P1002 过河卒 详细题解 搜索回溯+递归 [NOIP2002 普及组]

时间:2022-11-23 10:22:36浏览次数:58  
标签:NOIP2002 int 题解 xxx P1002 xx yyy && 100

题目描述

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

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

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

输入格式

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

输出格式

一个整数,表示所有的路径条数。

样例输入

6 6 3 3

样例输出

6
对于 100% 的数据,1 <= n, m <= 20,0 <= 马的坐标  <= 20。

【题目来源】

NOIP 2002 普及组第四题

题目分析

首先 A 点的坐标是(0,0)(0,0)!并且,卒只能向下或向右!

我们都清楚象棋中的马是如何跳的,跳的是“日”字形。以输入样例 6×6,马在 (3,3)(3,3) 为例,左至右分别为 x ,上至下分别为 y。

其中 C 点为马的控制点。我们不妨把马的控制点 a[x][y] 标记为 1。

样例共有 6 条路径:

大家仔细观察一下,马在第一排 (y=0)(y=0) 的走法有三种

  1. 第一排走到底,再走到目标点;
  2. 第一排走到第四个 0 的位置,这里大家可以看出有一种路径;
  3. 第一排走到倒数第二个 0 的位置,拐个弯到达目标位置;

这里总共 33 种走法; 在第一列 (x=0)(x=0) 的走法有两种,分别是:

  1. 第一列走到倒数第二个 0 的位置,拐个弯到达目标位置;
  2. 第一列走到第四个 0 的位置,大家可以看出有一种路径;
  3. 第一列走到底,再走到目标点;

这里总共有 3 种走法;加上前面的3种,共有6种!

看到这里,大家应该明白了题意,现在我们看输入输出:

  1. 输入前两个数,为棋盘的长宽;
  2. 输入后两个数,为马的坐标(x,y)(x,y);

代码实现

搜索回溯

一匹马

先判断出马的所有据点坐标
判断坐标是否越界
但考虑的细节很多,代码如下:
#include<bits/stdc++.h>
using namespace std;

int n,m,x,y;  //(n,m)=终点坐标  (x,y)=马坐标 
int sum=0; //计数 
bool a[21][21]={0}; //判断是否可走 

void dfs(int x,int y){
	for(int i=1;i<=2;i++){  //有两种情况(向上或向右走) 
		if(x<=n&&y<=m&&a[x][y]==0){
			int oldx=x,oldy=y;
			if(i==1){  //向下走 防止越界 判断下面的数是否被马占领 
				x++;
			}
			if(i==2){  //向右走 防止越界 判断右边的数是否被马占领
				y++;
			}
			if(x==n&&y==m){
				sum++;
			} 
			else{
				dfs(x,y);
			}
			x=oldx,y=oldy;  //返回原值 
		}
	}
}

int main(){
	
	cin>>n>>m>>x>>y;
	
	//判断马的据点
	a[x][y]=1;
	// P1
	if(x+2<=n&&y+1<=m){
		a[x+2][y+1]=1;
	}
	// P2
	if(x+1<=n&&y+2<=m){
		a[x+1][y+2]=1;
	}
	// P3
	if(x-1>=0&&y+2<=m){
		a[x-1][y+2]=1;
	}
	// P4
	if(x-2>=0&&y+1<=m){
		a[x-2][y+1]=1;
	}
	// P5
	if(x-2>=0&&y-1>=0){
		a[x-2][y-1]=1;
	}
	// P6
	if(x-1>=0&&y-2>=0){
		a[x-1][y-2]=1;
	}
	// P7
	if(x+1<=n&&y-2>=0){
		a[x+1][y-2]=1;
	}
	// P8
	if(x+2<=n&&y-1>=0){
		a[x+2][y-1]=1;
	}
	
	dfs(0,0); 
	
	cout<<sum;
	
	return 0;
} 
因为用的是搜索回溯而不是递归,所以会超时!
测试点如下:

两匹马

偷懒思路-两匹马

偷懒思路:
这个思路是我朋友想到的(所以没加注释,应该很好懂吧
把数组设大一点,把棋盘放到中间
这样就不用考虑越界了!代码如下:
#include<bits/stdc++.h>
using namespace std;

int s[10001][10001]={0},n,m,x1,y3,y2,x2,sum=0;

int sch(int x,int y){
	for(int i=1;i<=2;i++){
		if(s[x][y]==0&&x<=n+100&&y<=m+100){
			int p=x,l=y;
			if(i==1){
				x++;
			}
			if(i==2){
				y++;
			}
			if(n+100==x&&m+100==y){
				sum++;
			}
			else{
				sch(x,y);
			}
			x=p;
			y=l;
		}
	}
}

int main(){
	
	cin>>n>>m>>x1>>y3>>x2>>y2;
	s[x1+100][y3+100]=1; 
    s[x1+100-2][y3+100+1]=1;
	s[x1+100-2][y3+100-1]=1; 
	s[x1+100+1][y3+100-2]=1; 
	s[x1+100+1][y3+100+2]=1;  
	s[x1+100-1][y3+100+2]=1;  
	s[x1+100-1][y3+100-2]=1;
	s[x1+100+2][y3+100-1]=1;  
	s[x1+100+2][y3+100+1]=1;  
	s[x2+100-2][y2+100+1]=1;
	s[x2+100-2][y2+100-1]=1; 
	s[x2+100+1][y2+100-2]=1; 
	s[x2+100+1][y2+100+2]=1;  
	s[x2+100-1][y2+100+2]=1;  
	s[x2+100-1][y2+100-2]=1;
	s[x2+100+2][y2+100-1]=1;  
	s[x2+100+2][y2+100+1]=1;  
	s[x2+100][y2+100]=1;
	sch(100,100);
	cout<<sum; 
	
	return 0; 
} 
要注意的是,这种思路要从(100,100)开始
则 sch(100,100);

一般思路-两匹马

浅浅改一下代码就可以了!
#include<bits/stdc++.h>
using namespace std;

int n,m,xxx,yyy,xx,yy;  //(n,m)=终点坐标  (xx,yy)=第一匹马的坐标 (xxx,yyy)=第二匹马的坐标 
int sum=0; //计数 
bool a[21][21]={0}; //判断是否可走 

void dfs(int x,int y){
	for(int i=1;i<=2;i++){  //有两种情况(向上或向右走) 
		if(x<=n&&y<=m&&a[x][y]==0){
			int oldx=x,oldy=y;
			if(i==1){  //向下走 防止越界 判断下面的数是否被马占领 
				x++;
			}
			if(i==2){  //向右走 防止越界 判断右边的数是否被马占领
				y++;
			}
			if(x==n&&y==m){
				sum++;
			} 
			else{
				dfs(x,y);
			}
			x=oldx,y=oldy;  //返回原值 
		}
	}
}

int main(){
	
	cin>>n>>m>>xxx>>yyy>>xx>>yy;
	
	//判断第一匹马的据点(xx,yy)
	a[xxx][yyy]=1;
	// P1
	if(xxx+2<=n&&yyy+1<=m){
		a[xxx+2][yyy+1]=1;
	}
	// P2
	if(xxx+1<=n&&yyy+2<=m){
		a[xxx+1][yyy+2]=1;
	}
	// P3
	if(xxx-1>=0&&yyy+2<=m){
		a[xxx-1][yyy+2]=1;
	}
	// P4
	if(xxx-2>=0&&yyy+1<=m){
		a[xxx-2][yyy+1]=1;
	}
	// P5
	if(xxx-2>=0&&yyy-1>=0){
		a[xxx-2][yyy-1]=1;
	}
	// P6
	if(xxx-1>=0&&yyy-2>=0){
		a[xxx-1][yyy-2]=1;
	}
	// P7
	if(xxx+1<=n&&yyy-2>=0){
		a[xxx+1][yyy-2]=1;
	}
	// P8
	if(xxx+2<=n&&yyy-1>=0){
		a[xxx+2][yyy-1]=1;
	}
	
	//判断第二匹马马的据点(xxx,yyy) 把(xx,yy)改成(xxx,yyy)即可 
	a[xx][yy]=1;
	// P1
	if(xx+2<=n&&yy+1<=m){
		a[xx+2][yy+1]=1;
	}
	// P2
	if(xx+1<=n&&yy+2<=m){
		a[xx+1][yy+2]=1;
	}
	// P3
	if(xx-1>=0&&yy+2<=m){
		a[xx-1][yy+2]=1;
	}
	// P4
	if(xx-2>=0&&yy+1<=m){
		a[xx-2][yy+1]=1;
	}
	// P5
	if(xx-2>=0&&yy-1>=0){
		a[xx-2][yy-1]=1;
	}
	// P6
	if(xx-1>=0&&yy-2>=0){
		a[xx-1][yy-2]=1;
	}
	// P7
	if(xx+1<=n&&yy-2>=0){
		a[xx+1][yy-2]=1;
	}
	// P8
	if(xx+2<=n&&yy-1>=0){
		a[xx+2][yy-1]=1;
	}
	
	dfs(0,0); 
	
	cout<<sum;
	
	return 0;
} 
输入两匹马的位置都为(3,3)
结果仍然是6条路径,如下图 教程到这就结束了!
看我写的这么辛苦的份上,点个赞好吗~

递归代码(双马)

用递归实现更简单
递归两匹马code:
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,m,m1x,m1y,m2x,m2y,a[105][105],b[105][105]={0};
	cin>>n>>m>>m1x>>m1y>>m2x>>m2y;
	//马的据点 
	b[m1x][m1y]=1;
	if(m1x-2>=0&&m1y-1>=0){
		b[m1x-2][m1y-1]=1;
	}
	if(m1x-1>=0&&m1y-2>=0){
		b[m1x-1][m1y-2]=1;
	}
	if(m1x+1<=n&&m1y-2>=0){
		b[m1x+1][m1y-2]=1;
	}
	if(m1x+2<=n&&m1y-1>=0){
		b[m1x+2][m1y-1]=1;
	}
	if(m1x+2<=n&&m1y+1<=m){
		b[m1x+2][m1y+1]=1;
	}
	if(m1x+1<=n&&m1y+2<=m){
		b[m1x+1][m1y+2]=1;
	}
	if(m1x-1>=0&&m1y+2<=m){
		b[m1x-1][m1y+2]=1;
	}
	if(m1x-2>=0&&m1y+1<=m){
		b[m1x-2][m1y+1]=1;
	}
	
	b[m2x][m2y]=1;
	if(m2x-2>=0&&m2y-1>=0){
		b[m2x-2][m2y-1]=1;
	}
	if(m2x-1>=0&&m2y-2>=0){
		b[m2x-1][m2y-2]=1;
	}
	if(m2x+1<=n&&m2y-2>=0){
		b[m2x+1][m2y-2]=1;
	}
	if(m2x+2<=n&&m2y-1>=0){
		b[m2x+2][m2y-1]=1;
	}
	if(m2x+2<=n&&m2y+1<=m){
		b[m2x+2][m2y+1]=1;
	}
	if(m2x+1<=n&&m2y+2<=m){
		b[m2x+1][m2y+2]=1;
	}
	if(m2x-1>=0&&m2y+2<=m){
		b[m2x-1][m2y+2]=1;
	}
	if(m2x-2>=0&&m2y+1<=m){
		b[m2x-2][m2y+1]=1;
	}
	//递归边界 
	for(int i=0;i<=m;i++){
		a[0][i]=1;
	} 
	for(int i=0;i<=n;i++){
		a[i][0]=1;
	}
	//递归 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(b[i][j]==0){
				a[i][j]=a[i-1][j]+a[i][j-1];
			}
		}
	}
	cout<<a[n][m]<<endl; 
}

标签:NOIP2002,int,题解,xxx,P1002,xx,yyy,&&,100
From: https://www.cnblogs.com/HaoranZing/p/16917430.html

相关文章

  • Could not get a resource from the pool 问题解决
    Couldnotgetaresourcefromthepool问题解决今天测试项目的时候,界面提示Couldnotgetaresourcefromthepool 报错信息。登录后台,查询对应的java报错日志报......
  • 奇怪的主席树题 题解
    前言前置知识:可持久化线段树,字符串hash,线段树二分。Description给你一棵\(n\)个点以\(1\)为根节点的树,每一个节点都代表一个\(m\)个字符的字符串。每一个节点......
  • YACS 2022年11月月赛 乙组 T1 数对统计 题解
    好久没更了,闲话一句,我的初赛成绩只有$71.5$,$76$才能进$NOIP$,所以就更一篇吧首先先考虑暴力算法,枚举两个数,设这两个数为$x$和$y$,如果$f[x][y]=false$,那就标记为$t......
  • Vscode/Sublime C++ 打印中文乱码问题解决
    #include<iostream>usingnamespacestd;#ifdef_WIN32#include<windows.h>#endifintmain(){#ifdef_WIN32//控制台显示乱码纠正SetConsoleOutp......
  • CF433 & 434 题解
    比赛链接:https://codeforces.com/contest/434中国人出的浓度很高的一场kitaharaharuki-北原春希(WA2)KuriyamaMarai-栗山未来(境界的彼方)Ryouko-御门凉子(出包王女......
  • P3178 [HAOI2015]树上操作 的dfs序题解
    操作1:把某个节点x的点权增加a。操作2:把某个节点x为根的子树中所有点的点权都增加a。操作3:询问某个节点x到根的路径中所有点的点权和。//点修改+树修改,(点......
  • 常见问题解决方案
    1.Failedtofindthek3s-selinuxpolicy检查master机器上是否已经安装了不同版本的k3s-selinux或者selinux-policy工具包,建议将机器上相关包全部卸载以后重新执行安装。......
  • 问题解决:Unlink of file '.git/objects/pack/****' failed. Should I try again?
    gitpull拉取代码的时候遇到上面的错误,选择是或者否都不行,貌似说文件被占用了,也尝试用过找到.git里面对应的文件删除掉,也可以解决,不过文件占用多了,不可能一个个手动清......
  • CF1601 题解
    偶然看这一场的题目,忽然很有感觉,于是写了一下A题面考虑每一位可以单独分开考虑考虑单独的一位,每次要选\(m\)个位置,可能产生贡献的位置就是这位为1的数,设数量为\(x......
  • 【题解】TEST 22.11.21 - 计数(感谢强大艾尔法!!1)
    我们要求的是:\[\begin{aligned}G(x)&=\sum_{i\geq0}(i+n-m)!(-1)^{m-i}x^i\\G'(x)&=\sum_{i\geq1}i(i+n-m)!(-1)^{m-i}x^{i-1}\end{aligned}\]考虑凑\(\sum\limits......