万圣节后的早晨

万圣节后的早晨

分析

这题的关键在于奇妙的建图操作。

四个格子中至少有一个障碍物格子联通和障碍物也各自联通

因此,我们可以将空格单独提取出来,并且预处理出来所有的空格可以到的所有点。

这样就减少了状态空间,并且减少了每一次搜索的状态转移次数。

当然实现的方法很多,从最普通的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

posted @ 2022-03-14 20:56  艾特玖  阅读(61)  评论(0)    收藏  举报