[关闭]
@himnouth 2022-08-07T13:26:57.000000Z 字数 2984 阅读 277

题解

Atcoder Beginner Contest 257 Problem F 题解

题目链接:_

题目大意

个城镇,分别编号

条路,第条路连接城镇。每条路都是双向的。路人甲经过一条路即从路的一边走到路的另一边需要分钟。但对于一些道路,若其,则代表该道路的一边即不确定。

对于每一组,试解答问题如下:

若不确定的边连接,试求出点到点的最短距离,若点与点不联通,则输出

样例输入1

  1. 3 2
  2. 0 2
  3. 1 2

样例输出1

  1. -1 -1 2

样例解释1

如果所有的未确定道路都连接到城镇,则道路和道路都连接了城镇。是不可能从城镇走到城镇的。

如果所有的未确定道路都连接到城镇,则道路连接了号城镇自己,道路连接了城镇,也是不可能从城镇走到城镇的。

如果所有的未确定道路都连接到城镇,则道路连接了城镇,道路连接了城镇,于是可以从用分钟城镇走到城镇

因此输出为
请注意,确定了不确定的道路连接到的城镇,可能有一条道路连接城镇和城镇本身,也可能有多条道路连接同一对城镇。

样例输入2

  1. 5 5
  2. 1 2
  3. 1 3
  4. 3 4
  5. 4 5
  6. 0 2

样例输出2

  1. 3 3 3 3 2

解析

该题为一道图论题。
设点与点相连,U_i=j,则连接此二点后点到点的最短路径会有发生改变即经过路径和不发生改变即不经过路径两种情况。若经过路径,则有两种经过的可能,即:从点进入再从点离开和从点进入再从点离开,如图所示:

因此,我们可以先使用广度优先搜索算法求出点与点到任意点的距离,注意:包括点。然后枚举每一个点,从而计算出点到点经过路径的最短距离即经过路径且从点进入再从点离开的最短距离与经过路径且从点进入再从点离开的最短距离即的最小值,再将其与不经过路径的点的最短距离比较,求出二者最大值即可。

代码实现

以上算法代码实现如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define N 300005
  4. #define M 300005
  5. typedef long long ll;
  6. using LL=long long;
  7. #define INF 0x3f3f3f3f
  8. int dis_to_1[N],dis_to_N[N];
  9. int n,m;
  10. struct node{
  11. int to;
  12. };
  13. vector<node>q[N];
  14. void gdyxss_start_1(){
  15. bool vis[N];
  16. memset(vis,false,sizeof vis);
  17. memset(dis_to_1,INF,sizeof dis_to_1);
  18. queue<int>p;
  19. p.push(1);
  20. vis[1]=true;
  21. dis_to_1[1]=0;
  22. while(!p.empty()){
  23. int u=p.front();
  24. p.pop();
  25. for(int i=0;i<q[u].size();i++){
  26. int v=q[u][i].to;
  27. if(!vis[v]){
  28. dis_to_1[v]=dis_to_1[u]+1;
  29. vis[v]=1;
  30. p.push(v);
  31. }
  32. }
  33. }
  34. }
  35. void gdyxss_start_N(){
  36. bool vis[N];
  37. memset(vis,false,sizeof vis);
  38. memset(dis_to_N,INF,sizeof dis_to_N);
  39. queue<int>p;
  40. p.push(n);
  41. vis[n]=true;
  42. dis_to_N[n]=0;
  43. while(!p.empty()){
  44. int u=p.front();
  45. p.pop();
  46. for(int i=0;i<q[u].size();i++){
  47. int v=q[u][i].to;
  48. if(!vis[v]){
  49. dis_to_N[v]=dis_to_N[u]+1;
  50. vis[v]=1;
  51. p.push(v);
  52. }
  53. }
  54. }
  55. }
  56. int main(){
  57. std::ios::sync_with_stdio(0);
  58. cin>>n>>m;
  59. for(int i=1;i<=m;i++){
  60. int u,v;
  61. cin>>u>>v;
  62. q[u].push_back({v});
  63. q[v].push_back({u});
  64. }
  65. gdyxss_start_1();
  66. gdyxss_start_N();
  67. for(int i=1;i<=n;i++){
  68. int res=min(dis_to_1[0]+dis_to_N[i],dis_to_1[i]+dis_to_N[0]);
  69. res=min(res,dis_to_1[n]);
  70. if(res==INF) cout<<"-1"<<' ';
  71. else cout<<res<<" ";
  72. }
  73. return 0;
  74. }

注:以上代码使用动态数组存储图。

另附使用链表存储图代码如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=3e5+5,INF=0x3f3f3f3f; //INF表示正无穷,即两边不可到达
  4. int n,m;
  5. int he[N],ne[N<<1],go[N<<1],val[N<<1],tot; //前向星
  6. int vis[N],dis1[N],disn[N]; //vis为标记数组,dis1和disn分别表示从1和n开始的dis值
  7. inline void add(int a,int b,int c){
  8. ne[++tot]=he[a];he[a]=tot;go[tot]=b;val[tot]=c;
  9. } //加边
  10. inline void bfs(int s,int *dis){
  11. memset(vis,0,sizeof vis);
  12. dis[s]=0;
  13. queue<int>q;
  14. q.push(s);
  15. vis[s]=1;
  16. while(!q.empty()){
  17. int u=q.front();
  18. q.pop();
  19. for(int i=he[u];i;i=ne[i]){
  20. int v=go[i];
  21. if(!vis[v]){
  22. q.push(v);vis[v]=1;
  23. dis[v]=dis[u]+val[i]; //计算dis
  24. }
  25. }
  26. }
  27. }
  28. int main(){
  29. cin>>n>>m;
  30. for(int i=1;i<=m;i++){
  31. int u,v;
  32. scanf("%d%d",&u,&v);
  33. add(u,v,1);add(v,u,1);
  34. }
  35. memset(dis1,INF,sizeof dis1);
  36. memset(disn,INF,sizeof disn); //开始时全不可达
  37. bfs(1,dis1);
  38. bfs(n,disn); //bfs找到dis值
  39. for(int i=1;i<=n;i++){
  40. int ans=min(dis1[n],min(dis1[0]+disn[i],dis1[i]+disn[0])); //具体见图
  41. if(ans==INF) cout<<-1<<" "; //如果不可达
  42. else cout<<ans<<" ";
  43. }
  44. return 0;
  45. }

版权声明

本文样例解释1及使用链表存储图代码来源于彭震楠的题解,本文未经作者允许,不得随意转载,非商业转载需注明出处。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注