P17471 [ICPC 2018 Jiaozuo R] Honeycomb 题解
题目大意
蜂巢可以描述为欧几里得平面的正则镶嵌,其中每个内部顶点有三个六边形相交且第二列的第一个蜂房始终在第一列第一个蜂房的右下侧,以此类推。
一个蜂房可能去掉相邻蜂房之间的一些墙壁,但整个蜂巢仍然是闭合的。
整个蜂巢具体地说,一个蜂房由 \(6\) 个顶点和 \(6\) 条边描述,它的上边界和下边界由三个连续的减号表示,其余边界由一个左斜杠或右斜杠表示,如果一个边界被去掉原来的位置将被空格取代。
求从一个起点移动到一个指定的目的地可行路径所需经过的最少蜂房数(包括起点和终点)。
在起始蜂房的中心,使用字母 S 或 T 来标记该特殊蜂房,所有其他字符均为空格字符。
算法分析
思路非常简单,但实现有点复杂。
大概就是先枚举所有被去掉的边界,然后将两边的蜂巢连边,最后广搜找最短路。
要注意的细节有输入蜂巢的时候要用 getline 函数读取一整行,不然可能只会输入一部分;在输入前,还要使用 getline 函数读取一下前一行的空格。再有啥看代码注释吧。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
string s[N<<1];//存蜂巢形状
vector<int> E[N];
struct node{
int x,dep;
};
queue<node> q;
bool vis[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;cin>>T;
while(T--){//多组
while(q.size())q.pop();//清空
int n,m;cin>>n>>m;
for(int i=1;i<=n*m;i++)E[i].clear(),vis[i]=0;//一定不要用memset
getline(cin,s[1]);//把这一行的空格读了
for(int i=1;i<=n*4+3;i++)getline(cin,s[i]);
for(int i=1;i<=n*4+3;i++)s[i]=" "+s[i];//方便处理
int cnt=0,S,T;
for(int i=1;i<=m;i++){//先枚举列
for(int j=1;j<=n;j++){
cnt++;//编号
int xx=(j-1)*4+3+((i%2==0)?2:0);//这个坐标是目前这个蜂房的中心点
int xy=(i-1)*6+5;//这些坐标看样例找一下规律就行了
if(s[xx][xy]=='S')S=cnt;
if(s[xx][xy]=='T')T=cnt;//判起点和终点
if(j<n){//看能不能和下边结点连边
if(s[xx+2][xy]==' '){
E[cnt].push_back(cnt+1);//双向
E[cnt+1].push_back(cnt);
}
}
if(i<m){//看能不能和右上和右下结点连边
if(!((i%2==1)&&j==1)){
if(s[xx-1][xy+3]==' '){
int nxt=cnt+n;
if(i%2==1)nxt--;//观察样例可知
E[cnt].push_back(nxt);
E[nxt].push_back(cnt);
}
}
if(!((i%2==0)&&j==n)){
if(s[xx+1][xy+3]==' '){
int nxt=cnt+n;
if(i%2==0)nxt++;//这里要注意一点,是+1
E[cnt].push_back(nxt);
E[nxt].push_back(cnt);
}
}
}
}
}
q.push({S,0});//然后就跑bfs
vis[S]=1;
int ans=0;
while(q.size()){
node d=q.front();
q.pop();
if(d.x==T){//如果到终点了
ans=d.dep+1;
break;
}
for(auto v:E[d.x]){
if(vis[v])continue;
q.push({v,d.dep+1});
vis[v]=1;
}
}
if(!ans)ans=-1;//这就说明没有方案
cout<<ans<<'\n';
}
}

浙公网安备 33010602011771号