题解:P13710 [NWERC 2023] Klompendans
这一道题,当我看到题目描述的那张图是,就知道是一道标准的搜索。
众所周知搜索分为Breadth-First Search和Depth-First Search(小装一波)。
这道题的类型属于BFS,但是天生反骨的我用的是DFS,但还是有原因的,见下:
题意解释
这道题要求从一个n*n的矩阵的左上角开始,有两种运动方法:
1.从当前瓷砖移动到一个距离当前瓷砖沿一个轴方向 a 格、另一轴方向 b 格的瓷砖。注意:题目没有限定对印的轴和正负移动方向,故达成题目中说的8种方法
(感谢豆包为我答疑解惑),
代码如下(pair的第一个值表示x轴,第二个表示y轴):
mova[0] = {a, b};
mova[1] = {a, -b};
mova[2] = {-a, b};
mova[3] = {-a, -b};
mova[4] = {b, a};
mova[5] = {b, -a};
mova[6] = {-b, a};
mova[7] = {-b, -a};
2.从当前瓷砖移动到一个距离当前瓷砖沿一个轴方向 c 格、另一轴方向 d 格的瓷砖。同理,8种方法,(代码不展示,自己推一下,这是本题的难点之一)。
第一个点可以任选一种移动方法,后面的点必须选前面一个点不同的方案。
求一共有多少个点可到达。注意:题目要求所有可到达的点,不是一条路径上的所有点,一次从一个点出发一次跳跃可到达的点都是合法的,包含在答案之中。
同时,题目没有限制出发点的做法,所以两种选择可到达的点都包含在答案之中
我之所以使用DFS,是因为一个点的前一个点可能是任意一种情况,也就是说一个点可以同时有两种移动方法,使用BFS记录和处理过于复杂,因此我选择了DFS(当然还主要是懒)。
我的代码(58ms AC)
#include <bits/stdc++.h>
#define ll long long
#define LL long long
using namespace std;
const int N=502;
int n,a,b,c,d,ans=0;
bool vis[N][N][2]={0},viss[N][N]={0};//vis[x][y][t]表示(x,y)点是否在t情况下访问过了;viss[x][y]表示(x,y)是否被访问过,用于ans计数
pair<int,int>mova[8],movb[8];//表示两种情况的所有移动方案
bool check(int x,int y){//检查合法性
return x>0 && y>0 && x<=n && y<=n;
}
void dfs(int x,int y,int t){//(x,y)是点的坐标,t=0时是第一中移动类型,t=2是第二中移动类型
//检查点是否在图内
if(!check(x,y)){
return;
}
//如果这个点已经在t状态下被访问过了,就return
if(vis[x][y][t]){
return;
}
vis[x][y][t]=1;
//如果这个点在t=0和t=1两个状态都没有被访问过,就是一个新到的点,ans++
if(!viss[x][y]){
ans++;
viss[x][y]=1;
}
//去下一个点
for(int i=0;i<8;i++){
if(t==0){
dfs(x+mova[i].first,y+mova[i].second,1);
}else{
dfs(x+movb[i].first,y+movb[i].second,0);
}
}
}
int main(){
//输入
scanf("%d%d%d%d%d",&n,&a,&b,&c,&d);
// 初始化移动A的8个方向
mova[0] = {a, b};
mova[1] = {a, -b};
mova[2] = {-a, b};
mova[3] = {-a, -b};
mova[4] = {b, a};
mova[5] = {b, -a};
mova[6] = {-b, a};
mova[7] = {-b, -a};
// 初始化移动B的8个方向
movb[0] = {c, d};
movb[1] = {c, -d};
movb[2] = {-c, d};
movb[3] = {-c, -d};
movb[4] = {d, c};
movb[5] = {d, -c};
movb[6] = {-d, c};
movb[7] = {-d, -c};
//开始DFS(第一个点有两种选择),中间不需要memset!!!
dfs(1,1,0);
dfs(1,1,1);
//输出
printf("%lld",ans);
return 0;
}
完结撒花-!!!