题解 | #传送门#

传送门

https://ac.nowcoder.com/acm/problem/236210

这道题就是跑两遍堆优化版的Dijkstra,从1和n开始记录他们到每一个点的距离用dist1[]和dist2[]来记录。
最重要的是传送门该怎么样去存储:比如传送门编号为i,里面有a,b,c三个点,可以用a点去找到传送门编号,再从传送门编号里面去找对应的传送门。
比如 i 号传送门进入a点,那就add(a,i,t)和add(i,a,t)来存储数据,从a到i的一条边和从i到a的一条边,找到i号传送门编号,就可以找到其他对应的传送门但会发现从a走到b要先走到 i 再走到b到堆优化里面就相当于是d[ b ]  = d[ i ] + w [ a ]所以这是两倍距离,从b到c也是一样,只需要最后将结果就是实际距离的两倍。但会出现 i = 1和a = 1 的情况(举例),会出现两条边重合的现象,所以i要加上100000(传送门总数量)来将重边分开。
#include<bits/stdc++.h>
using namespace std;
const int N=200010,M=2000010;
typedef pair<int,int> PII;
int h[N],e[M],ne[M],w[M],idx=0;  
int dist1[N],dist2[N];
int st[N];
int n,m;
void add(int a,int b,int c)      //邻接表存数据
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void Dijkstra(int x,int *d)   
{
    memset(st,0,sizeof(st));
    priority_queue<PII,vector<PII>,greater<PII>>q;   //这里一定要用PII存两个数据,只存节点会报错,有知道的可以评论里面说一说,我也不知道
    q.push({0,x});
    d[x]=0;  
    while(q.size()){
        auto num=q.top();q.pop();
        int t=num.second;
        if(st[t])continue;
        st[t]=1;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(d[j]>d[t]+w[i]){
                d[j]=d[t]+w[i];  
                q.push({d[j],j});
            }
        }
    }
}
int main()
{
    memset(dist1,0x3f,sizeof dist1);
    memset(dist2,0x3f,sizeof dist2);
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int t,s;
        scanf("%d%d",&t,&s);
        int r;
        for(int j=1;j<=s;j++)
        {
            scanf("%d",&r);
            add(100000+i,r,t);   //记录从传送门类型为i的传送门到r的时间
            add(r,100000+i,t);   //记录从传送门r到传送门类型i的时间 ,,因为会有重边所以加上传送门数量排重。
                        //就相当于在两个同类型的传送门之间加了一个额外的点,传送门类型,距离也就翻倍了
        }
    }
    
    Dijkstra(1,dist1);
    Dijkstra(n,dist2);     //两遍DJ求dist1和dist2。
    
    int ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
      if(dist1[i]!=0x3f3f3f3f&&dist2[i]!=0x3f3f3f3f)
        ans=min(ans,max(dist1[i],dist2[i]));      //一个人走到某一个点可以停下来等另一个人,所以只需要得到两个人中最晚到这个点的时间,主意是两倍
    
    if(ans==0x3f3f3f3f)      //如果ans没更新那就是没有走到相同的点,输出-1.
        cout<<-1<<endl;
    else
        {
      cout<<ans/2<<endl;       //因为是两倍所以要除2.
      for(int i=1;i<=n;i++)
        if(max(dist1[i],dist2[i])==ans)       //在所有点中找是否有和上面一样时间的点,从1开始,字典序输出
          cout<<i<<' ';
        }
}

全部评论

相关推荐

点赞 评论 收藏
分享
10-27 17:26
东北大学 Java
点赞 评论 收藏
分享
4 收藏 评论
分享
牛客网
牛客企业服务