蓝桥杯2024年第十五届省赛真题-数字接龙
题目描述
小蓝最近迷上了一款名为《数字接龙》的迷宫游戏,游戏在一个大小为N × N 的格子棋盘上展开,其中每一个格子处都有着一个 0 . . . K − 1 之间的整数。游戏规则如下:
1. 从左上角 (0, 0) 处出发,目标是到达右下角 (N − 1, N − 1) 处的格子,每一步可以选择沿着水平/垂直/对角线方向移动到下一个格子。
2. 对于路径经过的棋盘格子,按照经过的格子顺序,上面的数字组成的序列要满足:0, 1, 2, . . . , K − 1, 0, 1, 2, . . . , K − 1, 0, 1, 2 . . . 。
3. 途中需要对棋盘上的每个格子恰好都经过一次(仅一次)。
4. 路径中不可以出现交叉的线路。例如之前有从 (0, 0) 移动到 (1, 1),那么再从 (1, 0) 移动到 (0, 1) 线路就会交叉。
为了方便表示,我们对可以行进的所有八个方向进行了数字编号,如下图2 所示;因此行进路径可以用一个包含 0 . . . 7 之间的数字字符串表示,如下图 1是一个迷宫示例,它所对应的答案就是:41255214。
现在请你帮小蓝规划出一条行进路径并将其输出。如果有多条路径,输出字典序最小的那一个;如果不存在任何一条路径,则输出 −1。
输入格式
第一行包含两个整数 N、K。接下来输入 N 行,每行 N 个整数表示棋盘格子上的数字。
输出格式
输出一行表示答案。如果存在答案输出路径,否则输出 −1。
样例输入
3 3
0 2 0
1 1 1
2 0 2
样例输出
41255214
提示
【样例说明】行进路径如图 1 所示。
【评测用例规模与约定】对于 80% 的评测用例:1 ≤ N ≤ 5。对于 100% 的评测用例:1 ≤ N ≤ 10,1 ≤ K ≤ 10。
思路:
注意:这里的字典序最小不是长度最短,是数字最小的意思。
使用DFS深度优先遍历寻找路径(dfs的三个参数分别是当前位置的坐标和该位置的值)
dfs中结束条件是判断值是否符合,并且只要找到一个答案就结
主要部分是通过for循环来遍历8个移动方向,这里要判断移动后的点是否合理
判断下一个点的函数:判断是否越界、是否已经访问、是否出现路径交叉
判断交叉:
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=20;
int n,k;
int a[N][N];//每格中的值
bool vis[N][N];//判断是否访问过
int dy[]={0,1,1,1,0,-1,-1,-1};
int dx[]={-1,-1,0,1,1,1,0,-1};
vector<int> path;//记录当前的路径编号
string ans;//最终答案
bool flag=0;//结束判断条件
//只要找到一个答案就停止遍历,因为本身就是从i从小到大开始遍历
//越往后字典序(数字值)越大,因此只要找到一个答案就停止(防止有一个数据集越界)
//检查是否可以移动到(x,y)位置
bool check(int x,int y,int dir)//x,y是下一个要访问的点,dir是从哪个方向到x,y(方向数组i的值)
{
//检查是否越界
if(x<0||x>=n||y<0||y>=n) return false;
//检查是否已访问
if(vis[x][y]) return false;
//对于斜向移动(由分析可知,这时i都是奇数),检查是否有交叉
if(dir%2!=0)
{
//上一个地点坐标
int sx=x-dx[dir];
int sy=y-dy[dir];
//判断是否交叉
if(vis[sx+dx[dir]][sy]&&vis[sx][sy+dy[dir]]) return false;
}
return true;
}
void dfs(int x,int y,int u)//u是当前期望的值0,1,2…k-1
{
//如果当前位置的值不等于期望值 ,返回
if(a[x][y]!=u) return ;
//如果已经访问了所有结点并到达终点
if(path.size()==n*n-1&&x==n-1&&y==n-1)//结束条件必不可少
{
string s;//置初值
for(auto i:path)
s+=i+'0';
ans=s;
flag=1;
return ;
}
//尝试8个方向的移动
for(int i=0;i<8;i++)
{
int tx=x+dx[i];//移动的下一个点
int ty=y+dy[i];
if(check(tx,ty,i))//检查下一个点是否合理
{
vis[tx][ty]=1;//标记访问
path.push_back(i);//加入编号集
dfs(tx,ty,(u+1)%k);
path.pop_back();//当条件不符合时恢复现场
vis[tx][ty]=0;
if(flag) return;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>a[i][j];
vis[0][0]=1;//标记起点已被访问
dfs(0,0,0);//当前点的位置x、y,期望值(0,1,2…k-1
if(!ans.empty())
cout<<ans<<endl;
else
cout<<"-1"<<endl;
return 0;
}