【1】题解:UVA1601 万圣节后的早晨 The Morning after Halloween
首先我们看原题面文件,有重要的东西。

这句话的意思是:在一个 \(2\times 2\) 的格子中,至少有一个是障碍物。
那么我们的棋盘空格数就从预期的 \(wh\) 降到了 \(\frac{wh}{4}\),这样我们动态开点的访问记录数组就从 \(w^3h^3\) 降到了 \(\frac{w^3h^3}{64}\),不会爆空间。
这里提几个细节:
- 换行符可能是
\n或\r所以读入时要判定,直到读入一个不为\n或\r的字符再存储。 - 鬼可以不动。
- 注意读题:两鬼不能交换。但是两个不存在的鬼可以相同位置也可以交换!
- 空间不要用太多。
其他没什么细节了。
#include<bits/stdc++.h>
using namespace std;
const int N=25,M=155;
int h,w,n,dot,sa,sb,sc,ea,eb,ec;
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
char a[N][N];
int idm[N][N];
bool vis[M][M][M];
vector<int>e[N*N];
queue<array<int,4>>q;
int main(){
while(cin>>w>>h>>n){
if(w==0&&h==0&&n==0)break;
memset(vis,0,sizeof(vis));
dot=sa=sb=sc=ea=eb=ec=0;
e[0].push_back(0);
for(int i=1;i<=h;i++){
for(int j=1;j<=w;j++){
a[i][j]=getchar();
while(a[i][j]=='\r'||a[i][j]=='\n')a[i][j]=getchar(); // 读入处理
if(a[i][j]!='#'){
idm[i][j]=++dot; // 动态开点
e[dot].clear();
e[dot].push_back(dot); // 鬼可以不动
if(a[i][j]=='a')sa=dot;
if(a[i][j]=='A')ea=dot;
if(a[i][j]=='b')sb=dot;
if(a[i][j]=='B')eb=dot;
if(a[i][j]=='c')sc=dot;
if(a[i][j]=='C')ec=dot;
}else idm[i][j]=0; // 注意清空方式
}
}
for(int i=1;i<=h;i++)
for(int j=1;j<=w;j++)if(a[i][j]!='#')
for(int d=0;d<4;d++){
int nx=i+dx[d],ny=j+dy[d];
if(nx<1||ny<1||nx>h||ny>w||!idm[nx][ny])continue;
e[idm[i][j]].push_back(idm[nx][ny]); // 建图
}
while(q.size())q.pop();
q.push({sa,sb,sc,0});
vis[sa][sb][sc]=1;
while(q.size()){
auto f=q.front();
q.pop();
if(f[0]==ea&&f[1]==eb&&f[2]==ec){
cout<<f[3]<<"\n";
break;
}
for(auto v0:e[f[0]])for(auto v1:e[f[1]])for(auto v2:e[f[2]]){
if(v0&&v1&&((v0==f[1]&&v1==f[0])||(v0==v1)))continue;
if(v0&&v2&&((v0==f[2]&&v2==f[0])||(v0==v2)))continue;
if(v1&&v2&&((v1==f[2]&&v2==f[1])||(v1==v2)))continue;
if(vis[v0][v1][v2])continue;
vis[v0][v1][v2]=1;
q.push({v0,v1,v2,f[3]+1});
}
}
}
return 0;
}

浙公网安备 33010602011771号