道路与航线

道路和航线

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

  • 解法一

    1. 看数据范围,以及一点分析,能够确定这是单源最短路,可选DJ与SPFA,但是图中存有负权边,只能SPFA了。

      However

      图片说明

      so,这里要用一个江湖人称“SLF”的优化,即当前节点到起点距离小于队首的时候,将此点插入队首,否则,正常插入队尾。感觉这种优化可能被用到的机会很少,但很可能正好解决掉了专门卡spfa的数据以及网格图。

    2. 解法一代码

      #include<deque>
      #include<algorithm>
      #include<cstring>
      #include<string>
      #include<cstdio>
      using namespace std;
      const int N=4e5+10;
      int n,m1,m2,s,tot;
      int h[N],nex[N],ver[N],pri[N],vis[N],dis[N];
      inline void add(int x,int y,int z)
      {
      nex[tot]=h[x];
      ver[tot]=y;
      pri[tot]=z;
      h[x]=tot++;
      }
      inline void spfa(int s)
      {
      memset(dis,0x3f,sizeof(dis));
      memset(vis,0,sizeof(vis));
      deque<int>q;
      dis[s]=0;vis[s]=1;
      q.push_back(s);
      while(q.size())
      {
       int now=q.front();q.pop_front();
       vis[now]=0;
       for (int i=h[now];~i;i=nex[i])
       {
           int j=ver[i];
           if(dis[j]>dis[now]+pri[i])
           {
               dis[j]=dis[now]+pri[i];
               if(!vis[j])
               {
                   vis[j]=1;
                   if(q.size()&&dis[j]<dis[q.front()]) q.push_front(j);
                   else q.push_back(j);
               }
           }
       }
      }
      }
      int main()
      {
      memset(h,-1,sizeof(h));
      scanf("%d%d%d%d",&n,&m1,&m2,&s);
      for (int i=1;i<=m1;i++)
      {
       int x,y,z;
       scanf("%d%d%d",&x,&y,&z);
       add(x,y,z),add(y,x,z);
      }
      for (int i=1;i<=m2;i++)
      {
       int x,y,z;
       scanf("%d%d%d",&x,&y,&z);
       add(x,y,z);
      }
      spfa(s);
      for (int i=1;i<=n;i++)
      {
       if(dis[i]==0x3f3f3f3f) puts("NO PATH");
       else printf("%d\n",dis[i]);
      }
      return 0;
      }
  • 解法二

    1. 注意到道路权值为正,航线权值为负,且有向边不会构成一个环,那么可以根据拓扑排序的思想,把每一个有道路连接的小镇归为一个连通块,然后在每一个联通块中连边,就能构成一个有向连通图,然后能在规定时间内跑出单源最短路,比解法一更快

    2. 解法二代码

      #include<bits/stdc++.h>
      using namespace std;
      const int N=3e4+10,M=2e5+10;
      int n,m1,m2,s,num,dfn;
      int h[N],bel[N],deg[N],dis[N];
      bool vis[N];
      struct nk
      {
      int val,pos;
      bool operator < (const nk &b)const
      {
       return val>b.val;
      }
      };
      struct E {int nex,to,dis;}e[M];
      vector<int>c[N];
      queue<int>q;
      inline void add(int u,int v,int w)
      {
      e[++num].nex=h[u];
      e[num].to=v;
      e[num].dis=w;
      h[u]=num;
      }
      inline void dfs(int x)
      {
      bel[x]=dfn,vis[x]=1;
      c[dfn].push_back(x);
      for(int i=h[x];i;i=e[i].nex)
      {
       int j=e[i].to;
       if(vis[j]) continue;
      
       dfs(j);
      }
      }
      inline void dj(int id)
      {
      priority_queue<nk>p;
      int len=c[id].size();
      for(int i=0;i<len;i++)
       p.push((nk){dis[c[id][i]],c[id][i]});
      while(p.size())
      {
       int now=p.top().pos;p.pop();
      
       if(vis[now]) continue;
       vis[now]=1;
       for(int i=h[now];i;i=e[i].nex)
       {
           int j=e[i].to,w=e[i].dis;
           if(dis[now]+w<dis[j])
           {
               dis[j]=dis[now]+w;
               if(bel[now]==bel[j]) p.push((nk){dis[j],j});
           }
           deg[bel[j]]--;
           if(!deg[bel[j]]&&bel[now]!=bel[j]) q.push(bel[j]);
       }
      }
      }
      int main()
      {
      scanf("%d%d%d%d",&n,&m1,&m2,&s);
      for (int i=1;i<=m1;i++)
      {
       int x,y,z;scanf("%d%d%d",&x,&y,&z);
       add(x,y,z),add(y,x,z);
      }
      //道路
      for (int i=1;i<=n;i++)
       if(!vis[i]) ++dfn,dfs(i);
      //划分一个连通块 
      for (int i=1;i<=m2;i++)
      {
       int x,y,z;
       scanf("%d%d%d",&x,&y,&z);
       add(x,y,z);deg[bel[y]]++;
      }
      //航线 
      memset(vis,0,sizeof(vis));
      memset(dis,0x3f,sizeof(dis));
      dis[s]=0,q.push(bel[s]);
      for(int i=1;i<=dfn;i++) if(!deg[i]) q.push(i);
      while(q.size())
      {
       int now=q.front();
       q.pop();dj(now);
      }
      for (int i=1;i<=n;i++)
      {
       if(dis[i]>=1e9) printf("NO PATH\n");
       else printf("%d\n",dis[i]);
      }
      return 0; 
      }
  • 后话

    好像不太会用格式。如果对内容或是格式有些许不适,还请指出(^▽^)

每日一题 文章被收录于专栏

每天的题,为了牛币

全部评论
大佬,我想问问为什么不能用floyd直接跑出s到每个点的最短距离
点赞 回复 分享
发布于 2022-06-03 13:33
看了您的三篇题解,学到了不少东西
点赞 回复 分享
发布于 2021-09-15 16:44
大佬,思路清晰。萌新学到了%%%%
点赞 回复 分享
发布于 2020-09-10 18:52

相关推荐

刚刷到字节跳动官方发的消息,确实被这波阵仗吓了一跳。在大家还在纠结今年行情是不是又“寒冬”的时候,字节直接甩出了史上规模最大的转正实习计划——ByteIntern。咱们直接看几个最硬的数,别被花里胡哨的宣传词绕晕了。首先是“量大”。全球招7000多人是什么概念?这几乎是把很多中型互联网公司的总人数都给招进来了。最关键的是,这次的资源分配非常精准:研发岗给了4800多个Offer,占比直接超过六成。说白了,字节今年还是要死磕技术,尤其是产品和AI领域,这对于咱们写代码的同学来说,绝对是今年最厚的一块肥肉。其次是大家最关心的“转正率”。官方直接白纸黑字写了:整体转正率超过50%。这意味着只要你进去了,不划水、正常干,每两个人里就有一个能直接拿校招Offer。对于2027届(2026年9月到2027年8月毕业)的同学来说,这不仅是实习,这简直就是通往大厂的快捷通道。不过,我也得泼盆冷水。坑位多,不代表门槛低。字节的实习面试出了名的爱考算法和工程实操,尤其是今年重点倾斜AI方向,如果你简历里有和AI相关的项目,优势还是有的。而且,转正率50%也意味着剩下那50%的人是陪跑的,进去之后的考核压力肯定不小。一句话总结:&nbsp;27届的兄弟们,别犹豫了。今年字节这是铁了心要抢提前批的人才,现在投递就是占坑。与其等到明年秋招去千军万马挤独木桥,不如现在进去先占个工位,把转正名额攥在手里。
喵_coding:别逗了 50%转正率 仔细想想 就是转正与不转正
字节7000实习来了,你...
点赞 评论 收藏
分享
评论
3
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务