万圣节后的早晨
分析
这题的关键在于奇妙的建图操作。
由四个格子中至少有一个障碍物和格子联通和障碍物也各自联通
因此,我们可以将空格单独提取出来,并且预处理出来所有的空格可以到的所有点。
这样就减少了状态空间,并且减少了每一次搜索的状态转移次数。
当然实现的方法很多,从最普通的BFS,到双向广搜,以及A*,我会分别贴出来,并加上解析。
其中有一些细节。
写的时候是按照三个鬼写的,因此当小于3的时候,惊人的操作来了
if(n<=2) {deg[cnt]=1;G[cnt][0]=cnt;s[2]=ed[2]=cnt++;};
if(n<=1) {deg[cnt]=1;G[cnt][0]=cnt;s[1]=ed[1]=cnt++;};
我们建立虚节点,使得不存在的鬼一直指向自己,这样就能永远合法了
AC_code
普通BFS
时间970ms
#include<bits/stdc++.h>
using namespace std;
struct Node
{
int a,b,c;
};
const int N = 200,INF = 0x3f3f3f3f;
char g[20][20];//存图
int G[N][5],dist[N][N][N],id[20][20];//G存的是每一个点能够到达的点的集合,dist是到达三个鬼当前状态的最短距离,id存的是每个图的编号
int deg[N];//deg存的是每个点所能连接的点的数量
int dx[5] = {0,0,1,0,-1},dy[5] = {0,1,0,-1,0};//方向数组
int w,h,n,s[3],ed[3];//s存的是三个鬼的点开始的标号,ed存的是三个鬼的点结束的标号
bool conflict(int a,int a2,int b,int b2)//判断是否发生冲突情况
{
return (a2==b2)||(a==b2&&b==a2);//没发生要去同一个点或者交换位置的情况
}
int bfs()
{
queue<Node> q;
q.push({s[0],s[1],s[2]});
memset(dist,-1,sizeof dist);
dist[s[0]][s[1]][s[2]] = 0;
while(q.size())
{
auto t = q.front();
q.pop();
int a = t.a,b = t.b,c = t.c;
if(a==ed[0]&&b==ed[1]&&c==ed[2]) break;
for(int i=0;i<deg[a];i++)
{
int a2 = G[a][i];
for(int j=0;j<deg[b];j++)
{
int b2 = G[b][j];
if(conflict(a,a2,b,b2)) continue;//判断是否a,b发生冲突
for(int k=0;k<deg[c];k++)
{
int c2 = G[c][k];
if(conflict(a,a2,c,c2)||conflict(b,b2,c,c2)) continue;//判断a,c与b,c是否冲突
if(dist[a2][b2][c2]==-1)
{
dist[a2][b2][c2] = dist[a][b][c] + 1;
q.push({a2,b2,c2});
}
}
}
}
}
return dist[ed[0]][ed[1]][ed[2]];
}
int main()
{
while(scanf("%d%d%d", &w, &h, &n),n&&w&&h)
{
for(int i = 0; i < h; i++)
{
scanf(" ");
for(int j=0;j<w;j++)
g[i][j]=getchar();
}//读图
int cnt = 0;
int x[200],y[200];
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
if(g[i][j]!='#')
{
x[cnt] = i,y[cnt] = j,id[i][j] = cnt;
if(islower(g[i][j])) s[g[i][j]-'a'] = cnt;
else if(isupper(g[i][j])) ed[g[i][j]-'A'] = cnt;
cnt++;
}//将空格提取存起来
for(int i=0;i<cnt;i++)//预处理出来所有点能到的点的集合
{
deg[i] = 0;
for(int j=0;j<5;j++)
{
int nx = x[i] + dx[j],ny = y[i] + dy[j];
if(nx<0||nx>=h||ny<0||ny>=w) continue;
if(g[nx][ny]=='#') continue;
G[i][deg[i]++] = id[nx][ny];
}
}
if(n<=2) {deg[cnt]=1;G[cnt][0]=cnt;s[2]=ed[2]=cnt++;};//建立虚拟点
if(n<=1) {deg[cnt]=1;G[cnt][0]=cnt;s[1]=ed[1]=cnt++;};
printf("%d\n",bfs());
}
return 0;
}
双向BFS
时间580ms
#include<bits/stdc++.h>
using namespace std;
const int N = 210;
struct Node
{
int a,b,c;
};
char g[N][N];
int G[N][5],dist[2][N][N][N];//dist建的是双向的数组
int id[20][20],x[N],y[N],deg[N];
int dx[5] = {0,0,1,0,-1},dy[5] = {0,1,0,-1,0};
int w,h,n,s[3],t[3];
bool conflict(int a,int a2,int b,int b2)
{
return (a2==b2)||(b2==a&&b==a2);
}
int extend(queue<Node> &q,int f1,int f2)//每次进行拓展的时候就将当前队列中的所有元素进行拓展
{
int len = q.size();
while(len--)
{
auto t = q.front();
q.pop();
int a = t.a,b = t.b,c = t.c;
for(int i=0;i<deg[a];i++)
{
int a2 = G[a][i];
for(int j=0;j<deg[b];j++)
{
int b2 = G[b][j];
if(conflict(a,a2,b,b2)) continue;
for(int k=0;k<deg[c];k++)
{
int c2 = G[c][k];
if(conflict(a,a2,c,c2)||conflict(b,b2,c,c2)) continue;
if(dist[f2][a2][b2][c2]!=-1) return dist[f2][a2][b2][c2]+dist[f1][a][b][c]+1;//若找到就直接返回
if(dist[f1][a2][b2][c2]!=-1) continue;
dist[f1][a2][b2][c2] = dist[f1][a][b][c] + 1;
q.push({a2,b2,c2});
}
}
}
}
return -1;
}
int bfs()
{
queue<Node> q[2];
memset(dist,-1,sizeof dist);
dist[0][s[0]][s[1]][s[2]] = 0;
dist[1][t[0]][t[1]][t[2]] = 0;
q[0].push({s[0],s[1],s[2]}),q[1].push({t[0],t[1],t[2]});
while(q[0].size()&&q[1].size())
{
int len1 = q[0].size(),len2 = q[1].size();
int t;
if(len1<len2) t = extend(q[0],0,1);//每次拓展元素少的队列
else t = extend(q[1],1,0);
if(t!=-1) return t;
}
return -1;
}
int main()
{
while(scanf("%d%d%d",&w,&h,&n),n||w||h)
{
for(int i=0;i<h;i++)
{
scanf(" ");
for(int j=0;j<w;j++) g[i][j]=getchar();
}
int cnt = 0;
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
if(g[i][j]!='#')
{
char c = g[i][j];
x[cnt] = i,y[cnt] = j,id[i][j]=cnt;
if(islower(c)) s[c-'a'] = cnt;
else if(isupper(c)) t[c-'A'] = cnt;
cnt++;
}
for(int i=0;i<cnt;i++)
{
deg[i] = 0;
for(int j=0;j<5;j++)
{
int nx = x[i] + dx[j],ny = y[i] + dy[j];
if(nx<0||nx>=h||ny<0||ny>=w) continue;
if(g[nx][ny]=='#') continue;
G[i][deg[i]++] = id[nx][ny];
}
}
if(n<=2)
{
deg[cnt]=1;
G[cnt][0]=cnt;
s[2] = t[2] = cnt++;
}
if(n<=1)
{
deg[cnt]=1;
G[cnt][0]=cnt;
s[1] = t[1] = cnt++;
}
printf("%d\n",bfs());
}
return 0;
}
A*
时间260ms
简单说一下A*的思路
首先预处理出来,所有鬼到其目标点的最短路。
将当前状态插入到堆中时,dist[i]设置为now_dist[i]+max(f[0] [i],f[1] [i],f[2] [i])
#include<bits/stdc++.h>
using namespace std;
const int N = 210;
struct Node
{
int a,b,c,dist;
bool operator<(const Node& W)const{
return dist>W.dist;
}
};
char g[20][20];
int G[N][5],dist[N][N][N],f[3][N];
bool st[N];
int id[20][20],x[200],y[200],deg[N];
int dx[5] = {0,0,1,0,-1},dy[5] = {0,1,0,-1,0};
int w,h,n,s[3],ed[3];
bool conflict(int a,int a2,int b,int b2)
{
return (a2==b2)||(b2==a&&b==a2);
}
int H(int a,int b,int c)
{
return max(f[0][a],max(f[1][b],f[2][c]));
}
int bfs()
{
priority_queue<Node> q;
q.push({s[0],s[1],s[2],H(s[0],s[1],s[2])});
memset(dist,0x3f,sizeof dist);
dist[s[0]][s[1]][s[2]] = 0;
while(q.size())
{
auto t = q.top();
q.pop();
int a = t.a,b = t.b,c = t.c;
// cout<<a<<" "<<b<<" "<<c<<endl;
if(a==ed[0]&&b==ed[1]&&c==ed[2]) return dist[a][b][c];
for(int i=0;i<deg[a];i++)
{
int a2 = G[a][i];
for(int j=0;j<deg[b];j++)
{
int b2 = G[b][j];
if(conflict(a,a2,b,b2)) continue;
for(int k=0;k<deg[c];k++)
{
int c2 = G[c][k];
if(conflict(a,a2,c,c2)||conflict(b,b2,c,c2)) continue;
if(dist[a2][b2][c2]>dist[a][b][c]+1)
{
dist[a2][b2][c2] = dist[a][b][c] + 1;
q.push({a2,b2,c2,dist[a2][b2][c2]+H(a2,b2,c2)});
}
}
}
}
}
return -1;
}
void dijkstra(int u)
{
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;
memset(f[u],0x3f,sizeof f[u]);
memset(st,0,sizeof st);
f[u][ed[u]]=0;
q.push({0,ed[u]});
while(q.size())
{
auto t = q.top();
q.pop();
int ver = t.second;
if(st[ver]) continue;
st[ver] = 1;
for(int i=0;i<deg[ver];i++)
{
int ver2 = G[ver][i];
if(f[u][ver2]>f[u][ver]+1)
{
f[u][ver2] = f[u][ver] + 1;
q.push({f[u][ver2],ver2});
}
}
}
}
int main()
{
while(scanf("%d%d%d",&w,&h,&n),n||w||h)
{
for(int i=0;i<h;i++)
{
scanf(" ");
for(int j=0;j<w;j++) g[i][j]=getchar();
}
int cnt = 0;
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
if(g[i][j]!='#')
{
char c = g[i][j];
x[cnt] = i,y[cnt] = j,id[i][j]=cnt;
if(islower(c)) s[c-'a'] = cnt;
else if(isupper(c)) ed[c-'A'] = cnt;
cnt++;
}
for(int i=0;i<cnt;i++)
{
deg[i] = 0;
for(int j=0;j<5;j++)
{
int nx = x[i] + dx[j],ny = y[i] + dy[j];
if(nx<0||nx>=h||ny<0||ny>=w) continue;
if(g[nx][ny]=='#') continue;
G[i][deg[i]++] = id[nx][ny];
}
}
if(n<=2)
{
deg[cnt]=1;
G[cnt][0]=cnt;
s[2] = ed[2] = cnt++;
}
if(n<=1)
{
deg[cnt]=1;
G[cnt][0]=cnt;
s[1] = ed[1] = cnt++;
}
for(int i=0;i<3;i++)
dijkstra(i);
printf("%d\n",bfs());
}
return 0;
}
顺便还拿了一手最快速度,hh


浙公网安备 33010602011771号