@himnouth
2022-08-07T13:26:57.000000Z
字数 2984
阅读 277
题解
题目链接:_
有个城镇,分别编号。
有条路,第条路连接城镇和。每条路都是双向的。路人甲经过一条路即从路的一边走到路的另一边需要分钟。但对于一些道路,若其,则代表该道路的一边即不确定。
对于每一组,试解答问题如下:
若不确定的边连接,试求出点到点的最短距离,若点与点不联通,则输出。
3 20 21 2
-1 -1 2
如果所有的未确定道路都连接到城镇,则道路和道路都连接了城镇和。是不可能从城镇走到城镇的。
如果所有的未确定道路都连接到城镇,则道路连接了号城镇自己,道路连接了城镇和,也是不可能从城镇走到城镇的。
如果所有的未确定道路都连接到城镇,则道路连接了城镇和,道路连接了城镇和,于是可以从用分钟城镇走到城镇:
因此输出为 。
请注意,确定了不确定的道路连接到的城镇,可能有一条道路连接城镇和城镇本身,也可能有多条道路连接同一对城镇。
5 51 21 33 44 50 2
3 3 3 3 2
该题为一道图论题。
设点与点相连,U_i=j,则连接此二点后点到点的最短路径会有发生改变即经过路径和不发生改变即不经过路径两种情况。若经过路径,则有两种经过的可能,即:从点进入再从点离开和从点进入再从点离开,如图所示:
因此,我们可以先使用广度优先搜索算法求出点与点到任意点的距离,注意:包括点。然后枚举每一个点,从而计算出点到点经过路径的最短距离即经过路径且从点进入再从点离开的最短距离与经过路径且从点进入再从点离开的最短距离即与的最小值,再将其与不经过路径的点到的最短距离比较,求出二者最大值即可。
以上算法代码实现如下:
#include<bits/stdc++.h>using namespace std;#define N 300005#define M 300005typedef long long ll;using LL=long long;#define INF 0x3f3f3f3fint dis_to_1[N],dis_to_N[N];int n,m;struct node{int to;};vector<node>q[N];void gdyxss_start_1(){bool vis[N];memset(vis,false,sizeof vis);memset(dis_to_1,INF,sizeof dis_to_1);queue<int>p;p.push(1);vis[1]=true;dis_to_1[1]=0;while(!p.empty()){int u=p.front();p.pop();for(int i=0;i<q[u].size();i++){int v=q[u][i].to;if(!vis[v]){dis_to_1[v]=dis_to_1[u]+1;vis[v]=1;p.push(v);}}}}void gdyxss_start_N(){bool vis[N];memset(vis,false,sizeof vis);memset(dis_to_N,INF,sizeof dis_to_N);queue<int>p;p.push(n);vis[n]=true;dis_to_N[n]=0;while(!p.empty()){int u=p.front();p.pop();for(int i=0;i<q[u].size();i++){int v=q[u][i].to;if(!vis[v]){dis_to_N[v]=dis_to_N[u]+1;vis[v]=1;p.push(v);}}}}int main(){std::ios::sync_with_stdio(0);cin>>n>>m;for(int i=1;i<=m;i++){int u,v;cin>>u>>v;q[u].push_back({v});q[v].push_back({u});}gdyxss_start_1();gdyxss_start_N();for(int i=1;i<=n;i++){int res=min(dis_to_1[0]+dis_to_N[i],dis_to_1[i]+dis_to_N[0]);res=min(res,dis_to_1[n]);if(res==INF) cout<<"-1"<<' ';else cout<<res<<" ";}return 0;}
注:以上代码使用动态数组存储图。
另附使用链表存储图代码如下:
#include<bits/stdc++.h>using namespace std;const int N=3e5+5,INF=0x3f3f3f3f; //INF表示正无穷,即两边不可到达int n,m;int he[N],ne[N<<1],go[N<<1],val[N<<1],tot; //前向星int vis[N],dis1[N],disn[N]; //vis为标记数组,dis1和disn分别表示从1和n开始的dis值inline void add(int a,int b,int c){ne[++tot]=he[a];he[a]=tot;go[tot]=b;val[tot]=c;} //加边inline void bfs(int s,int *dis){memset(vis,0,sizeof vis);dis[s]=0;queue<int>q;q.push(s);vis[s]=1;while(!q.empty()){int u=q.front();q.pop();for(int i=he[u];i;i=ne[i]){int v=go[i];if(!vis[v]){q.push(v);vis[v]=1;dis[v]=dis[u]+val[i]; //计算dis}}}}int main(){cin>>n>>m;for(int i=1;i<=m;i++){int u,v;scanf("%d%d",&u,&v);add(u,v,1);add(v,u,1);}memset(dis1,INF,sizeof dis1);memset(disn,INF,sizeof disn); //开始时全不可达bfs(1,dis1);bfs(n,disn); //bfs找到dis值for(int i=1;i<=n;i++){int ans=min(dis1[n],min(dis1[0]+disn[i],dis1[i]+disn[0])); //具体见图if(ans==INF) cout<<-1<<" "; //如果不可达else cout<<ans<<" ";}return 0;}
本文样例解释1及使用链表存储图代码来源于彭震楠的题解,本文未经作者允许,不得随意转载,非商业转载需注明出处。