码迷,mamicode.com
首页 > 其他好文 > 详细

题解 CF241E Flights

时间:2021-04-07 10:37:30      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:add   复杂度   min   std   需要   情况   code   while   bellman   

关于此题有一种精简的写法。

首先我们分析题面,可以发现如果最后可以达到使 \(1\)\(n\) 的路径距离都相等,那么从 \(1\) 到任意一个 \(1\)\(n\) 的路径上的节点的路径也都相等。所以我们设 \(dis[u]\)\(1\) 到 点 \(u\) 的路径长度,\(dis[v]\)\(1\)\(u\) 点连向的那个点的路径长度。因为边权要么为 \(1\) 要么为 \(2\),所以我们可以得到:\(1 \leq dis[v]-dis[u] \leq 2\),即 \(\begin{cases}dis[v]\leq dis[u]+2 \\ dis[u] \leq dis[v]-1 \end{cases}\)

所以我们就建一条 \(u\)\(v\) 的边权为 \(2\) 的边,建一条 \(v\)\(u\) 的边权为 \(-1\) 的边,然后就可以跑最短路求解出答案了(注意要判负环无解的情况)。

这里其实没必要去跑 \(spfa\),因为 \(n,m\) 都很小,且 \(spfa\) 找负环的时间复杂度是 \(O(nm)\) 的,所以我用了只有五行的 \(Bellman-Ford\) 算法,时间复杂度为 \(O(nm+m)\)

但是对于不在 \(1\)\(n\) 路径上的点,我们需要找出来并不管他们,我就用了一遍 \(dfs\) 解决了,没必要建反图跑两遍 \(dfs\) 再来找打了两次标记的点。

代码精简,运行也快,因为 \(spfa\) 在随机数据下的 \(O(km)\) 复杂度,所以还是略慢于跑了两遍 \(dfs\)\(spfa\)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
  int ans=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch==‘-‘) f=-f;ch=getchar();}
  while(isdigit(ch)){ans=(ans<<1)+(ans<<3)+ch-48;ch=getchar();}
  return ans*f;
}
const int N=5005;
int n,m;
int tag[N],vis[N];
int hd[N],nx[N],to[N],from[N],tot;
void adde(int u,int v){
  nx[++tot]=hd[u];to[tot]=v;hd[u]=tot,from[tot]=u;
}
int hd2[N],nx2[N<<1],to2[N<<1],tot2,val[N<<1],from2[N<<1];
void adde2(int u,int v,int w){
  nx2[++tot2]=hd2[u];to2[tot2]=v;hd2[u]=tot2,val[tot2]=w,from2[tot2]=u;
}
void dfs(int u){
  if(u==n) return;
  for(int i=hd[u];i;i=nx[i]){
    int v=to[i];
    if(!vis[v]) vis[v]=1,dfs(v);
    if(tag[v]) tag[u]=1;
  }
}
int dis[N];
int main(){
  n=read(),m=read();
  for(int i=1;i<=m;i++){
    int x=read(),y=read();
    adde(x,y);
  }
  tag[n]=1;
  dfs(1);
  for(int i=1;i<=m;i++){
    if(tag[from[i]]&&tag[to[i]]){
      adde2(from[i],to[i],2);
      adde2(to[i],from[i],-1);
    }
  }
  memset(dis,0x7f,sizeof(dis));
  dis[1]=0;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=tot2;j++)
      dis[to2[j]]=min(dis[to2[j]],dis[from2[j]]+val[j]);
  for(int i=1;i<=tot2;i++)
    if(dis[from2[i]]+val[i]<dis[to2[i]]) return printf("No\n"),0;
  printf("Yes\n");
  for(int i=1;i<=m;i++){
    if(tag[from[i]]&&tag[to[i]]) printf("%d\n",dis[to[i]]-dis[from[i]]);
    else printf("1\n");
  }
  return 0;
}

题解 CF241E Flights

标签:add   复杂度   min   std   需要   情况   code   while   bellman   

原文地址:https://www.cnblogs.com/Quick-Kk/p/14618428.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!