44分,其他全WA,救救孩子
查看原帖
44分,其他全WA,救救孩子
221923
RealXyy楼主2020/8/18 15:40

思路是把迷宫转换成图,相邻格之间建权为1的边,传送点之间建权为0的边,然后跑一遍Dijkstra;考虑到会有直接走过传送点的情况,最后循着路线碰到这样的情况就把答案+2(先出传送点,再回来)。 但是为什么就WA了一堆呢……

#include <bits/stdc++.h>
using namespace std;
int n,m;//高,宽
int G[310][310];//原始图(迷宫)
int num;//新图节点的数量
int head[90010],nxt[400010],To[400010],w[400010],tot;//前向星存图
int tp[30];//传送点
int st,ed;//起止点
int d[90010];//dijkstra距离
int vis[90010];//dijkstra是否被访问过
int pre[90010];
int isTp[90010];
int nTp[30];
map<pair<int, int> ,int>id;//迷宫中(i,j)对应新图的节点编号,id[make_pair(i,j)]
void AddEdge(int fr,int to,int v){//前向星存图(从fr到to建一条长度为v的边)
    nxt[++tot]=head[fr];
    head[fr]=tot;
    To[tot]=to;
    w[tot]=v;
}
int HadId(int i,int j){//是否已经分配了Id
    return id.count(make_pair(i,j));
}
int Id(int i,int j){//(i,j)的编号
    return id[make_pair(i,j)];
}
void SetId(int i,int j){//分配id给(i,j)
    id[make_pair(i,j)]=++num;
    // cout<<"^%%";
}
void Build(int fi,int fj,int ti,int tj){//从(fi,fj)到(ti,tj)建边
    if(ti<=0 || ti>n || tj<=0 ||tj>m)return;//(ti,tj)是否越界
    if(G[ti][tj]==-1)return;//(ti,tj)是否为墙
    if(!HadId(ti,tj))SetId(ti,tj);//(ti,tj)是否已有编号
    AddEdge(Id(fi,fj),Id(ti,tj),1);//从(fi,fj)到(ti,tj)建边
}
void BDnormal(int i,int j){//向四个方向尝试建边
    Build(i,j,i+1,j);
    Build(i,j,i-1,j);
    Build(i,j,i,j+1);
    Build(i,j,i,j-1);
}
int main(){
    // freopen("4.txt","w",stdout);
    scanf("%d%d",&n,&m);//输入宽、高
    for(int i=1;i<=n;i++){
        string temp;//读入每一行
        cin>>temp;
        temp=" "+temp;//便于读取,在字符串前加一个空格以将每一行开头的下标置为1
        for(int j=1;j<=m;j++){
            if(temp[j]=='#')G[i][j]=-1;//-1表示墙
            else if(temp[j]=='=')G[i][j]=-2;//-2表示出口ed
            else if(temp[j]=='@')G[i][j]=-3;//-3表示开头st
            else if(temp[j]=='.')G[i][j]=-4;//-4表示空
            else G[i][j]=temp[j]-'A';//>=0表示传送机
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(G[i][j]==-1)continue;
            if(!HadId(i,j))SetId(i,j);//如果(i,j)没有编号,分配给它
            if(G[i][j]==-2)
                ed=Id(i,j);//如果为出口,设置ed为其编号
            else if(G[i][j]==-3)
                st=Id(i,j);//如果为出发点,设置st为其编号
            else if(G[i][j]>=0){//如果为传送机
                isTp[Id(i,j)]=G[i][j];
                nTp[G[i][j]]++;
                if(tp[G[i][j]]){//如果已经记录过同类型传送机
                    AddEdge(Id(i,j),tp[G[i][j]],0);//建双向边
                    AddEdge(tp[G[i][j]],Id(i,j),0);
                }
                else tp[G[i][j]]=Id(i,j);//如果没有记录过该类传送机,记录之
            }
            BDnormal(i,j);//尝试向四周建边
        }
    priority_queue<pair<int,int> >q;//d,id
    for(int i=1;i<=num;i++)d[i]=0x7fffffff/2;//初始化
    d[st]=0;
    q.push(make_pair(0,st));//将起始点入堆
    while(!q.empty()){//堆优化的dijkstra
        int now=q.top().second;
        q.pop();
        if(vis[now])continue;
        vis[now]=1;
        for(int i=head[now];i;i=nxt[i]){
            int v=To[i];
            if(d[now]+w[i]<d[v]){
                d[v]=d[now]+w[i];
                q.push(make_pair(-d[v],v));
                pre[v]=now;
            }
        }
    }
    int n1,n2,n3;
    for(int i=ed;pre[pre[i]];i=pre[i]){
        n1=i;n2=pre[i];n3=pre[pre[i]];
        if(isTp[n2]&& isTp[n1]!=isTp[n2] && isTp[n2]!=isTp[n3])
            if(nTp[isTp[n2]]){d[ed]+=2;}
    }
    printf("%d\n",d[ed]);
    return 0;
}
2020/8/18 15:40
加载中...