寒假Day7:最短路
HDU2112-HDU Today-dijkstra
题面:
经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强。这时候,XHD夫妇也退居了二线,并在风景秀美的诸暨市浬浦镇陶姚村买了个房子,开始安度晚年了。 这样住了一段时间,徐总对当地的交通还是不太了解。有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车(其实徐总自己有车,却一定要与民同乐,这就是徐总的性格)。 徐总经常会问蹩脚的英文问路:“Can you help me?”。看着他那迷茫而又无助的眼神,热心的你能帮帮他吗? 请帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。 Input 输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000); 第二行有徐总的所在地start,他的目的地end; 接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)。 note:一组数据中地名数不会超过150个。 如果N==-1,表示输入结束。 Output 如果徐总能到达目的地,输出最短的时间;否则,输出“-1”。
样例:
Sample Input 6 xiasha westlake xiasha station 60 xiasha ShoppingCenterofHangZhou 30 station westlake 20 ShoppingCenterofHangZhou supermarket 10 xiasha supermarket 50 supermarket westlake 10 -1 Sample Output 50 Hint: The best route is: xiasha->ShoppingCenterofHangZhou->supermarket->westlake
题意:问给定的起点到终点的最短时间。
思路:dijkstra模板
注意:坑比较多
- 起点和终点可能是同一个点
- 双向边且取最小值
- 有map的应用,用于给定的起点和终点都是字符串,给定经过的点也是以字符串的形式存储
sum=0; string st,en; cin>>st>>en; mp[st]=++sum; mp[en]=++sum; for(int i=1; i<=n; i++) { string ss,ee; int t; cin>>ss>>ee>>t; if(!mp[ss]) mp[ss]=++sum; if(!mp[ee]) mp[ee]=++sum; if(e[mp[ss]][mp[ee]]>t||e[mp[ee]][mp[ss]]>t) e[mp[ss]][mp[ee]]=e[mp[ee]][mp[ss]]=t; }
顺带回顾一下dijkstra
1 void dijkstra() 2 { 3 for(int i=1;i<=sum;i++) 4 { 5 dist[i]=e[1][i]; 6 book[i]=0; 7 } 8 book[1]=1,dist[1]=0; 9 int u; 10 for(int i=2;i<=sum;i++) 11 { 12 int minn=inf; 13 for(int j=1;j<=sum;j++) 14 { 15 if(book[j]==0&&minn>dist[j]) 16 { 17 u=j; 18 minn=dist[j]; 19 } 20 } 21 if(minn==inf) 22 break; 23 book[u]=1; 24 for(int j=1;j<=sum;j++) 25 { 26 if(book[j]==0&&dist[u][j]<inf) 27 { 28 if(dist[u]+e[u][j]<dist[j]) 29 dist[j]=dist[u]+e[u][j]; 30 } 31 } 32 } 33 34 }
AC代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<map> 7 #include<cmath> 8 using namespace std; 9 #define inf 0x3f3f3f3f 10 const int N=1e4+20; 11 12 int e[N][N],dist[N]; 13 int sum; 14 bool book[N]; 15 map<string,int>mp; 16 17 void init() 18 { 19 for(int i=1;i<=150;i++) 20 { 21 for(int j=1;j<=150;j++) 22 { 23 if(i==j) 24 e[i][j]=0; 25 else 26 e[i][j]=inf; 27 } 28 } 29 } 30 31 void dijkstra() 32 { 33 for(int i=1;i<=sum;i++) 34 { 35 dist[i]=e[1][i]; 36 book[i]=0; 37 } 38 book[1]=1,dist[1]=0; 39 int u; 40 for(int i=2;i<=sum;i++) 41 { 42 int minn=inf; 43 for(int j=1;j<=sum;j++) 44 { 45 if(book[j]==0&&minn>dist[j]) 46 { 47 u=j; 48 minn=dist[j]; 49 } 50 } 51 if(minn==inf) 52 break; 53 book[u]=1; 54 for(int j=1;j<=sum;j++) 55 { 56 if(book[j]==0) 57 { 58 dist[j]=min(dist[j],dist[u]+e[u][j]); 59 } 60 } 61 } 62 63 } 64 65 int main() 66 { 67 ios::sync_with_stdio(false); 68 int n; 69 while(cin>>n) 70 { 71 if(n==-1) 72 break; 73 mp.clear(); 74 init(); 75 sum=0; 76 string st,en; 77 cin>>st>>en; 78 mp[st]=++sum; 79 mp[en]=++sum; 80 for(int i=1;i<=n;i++) 81 { 82 string ss,ee; 83 int t; 84 cin>>ss>>ee>>t; 85 if(!mp[ss]) 86 mp[ss]=++sum; 87 if(!mp[ee]) 88 mp[ee]=++sum; 89 if(e[mp[ss]][mp[ee]]>t||e[mp[ee]][mp[ss]]>t) 90 e[mp[ss]][mp[ee]]=e[mp[ee]][mp[ss]]=t; 91 } 92 //cout<<"---"<<sum<<endl; 93 dijkstra(); 94 if(st==en) 95 { 96 cout<<0<<endl; 97 continue; 98 } 99 //int w=e[1][2]; 100 int w=dist[2]; 101 if(w!=inf) 102 cout<<w<<endl; 103 else 104 cout<<"-1"<<endl; 105 } 106 return 0; 107 }
HDU6714-最短路2-Floyd
- 法一:一种是用dijkstra写的,但是我没看懂。。。
参考博客:https://blog.csdn.net/birdmanqin/article/details/100068433
https://www.cnblogs.com/iat14/p/11408298.html
- 法二:一种用floyd写的(以下思路和代码是用floyd写的)
题面:
小 A 是社团里的工具人,有一天他的朋友给了他一个 n 个点,m 条边的正权连通无向图,要他计算所有点两两之间的最短路。 作为一个工具人,小 A 熟练掌握着 floyd 算法,设 w[i][j] 为原图中 (i,j) 之间的权值最小的边的权值,若没有边则 w[i][j]=无穷大。特别地,若 i=j,则 w[i][j]=0。 Floyd 的 C++ 实现如下: ```c++ for(int k=1;k<=p;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) w[i][j]=min(w[i][j],w[i][k]+w[k][j]); ``` 当 p=n 时,该代码就是我们所熟知的 floyd,然而小 A 为了让代码跑的更快点,所以想减少 p 的值。 令 Di,j 为最小的非负整数 x,满足当 p=x 时,点 i 与点 j 之间的最短路被正确计算了。 现在你需要求 ∑ni=1∑nj=1Di,j,虽然答案不会很大,但为了显得本题像个计数题,你还是需要将答案对 998244353 取模后输出。 Input 第一行一个正整数 T(T≤30) 表示数据组数 对于每组数据: 第一行两个正整数 n,m(1≤n≤1000,m≤2000),表示点数和边数。 保证最多只有 5 组数据满足 max(n,m)>200 接下来 m 行,每行三个正整数 u,v,w 描述一条边权为 w 的边 (u,v),其中 1≤w≤109 Output 输出 T 行,第 i 行一个非负整数表示第 i 组数据的答案 Sample Input 1 4 4 1 2 1 2 3 1 3 4 1 4 1 1 Sample Output 6
题意:求所有顶点N中任意两点最短路中松弛点中最小值的和
注意:
- 用floyd很容易超时,这个地方处理很好。但是得放在第二层循环下,放在第三层会超时;
if(e[i][k]>inff) continue;
- 虽然每条边给的范围是109,但是由于floyd中会进行边的松弛,也就是会进行相加,所以需要开数组为ll;
- 因为大部分数据开的为ll,所以在对于边的处理应该初始化为ll型的无穷大,也就是0x3f3f3f3f3f3f3f3f(8个3f),约19位;
- 在处理数据的时候,第三层for循环到 j<i 即可,之后的结果*2,因为是双向边,所以后面的和前面一样也就无需重复处理了。
代码如下:
1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 #include<algorithm> 5 #include<queue> 6 #include<vector> 7 #include<map> 8 #include<cmath> 9 using namespace std; 10 #define inf 0x3f3f3f3f 11 #define inff 0x3f3f3f3f3f3f3f3f 12 const int N=1010; 13 #define mod 998244353 14 typedef long long ll; 15 16 ll e[N][N],dist[N],a[N][N]; 17 int n,m; 18 bool book[N]; 19 map<string,int>mp; 20 21 void init() 22 { 23 for(int i=1;i<=n;i++) 24 { 25 for(int j=1;j<=n;j++) 26 { 27 if(i==j) 28 e[i][j]=0; 29 else 30 e[i][j]=inff; 31 } 32 } 33 } 34 35 int main() 36 { 37 ios::sync_with_stdio(false); 38 int t; 39 cin>>t; 40 while(t--) 41 { 42 cin>>n>>m; 43 memset(a,0,sizeof(a)); 44 init(); 45 for(int i=1;i<=m;i++) 46 { 47 ll u,v,w; 48 cin>>u>>v>>w; 49 e[u][v]=e[v][u]=min(e[u][v],w); 50 } 51 ll ans=0; 52 for(int k=1;k<=n;k++) 53 { 54 for(int i=1;i<=n;i++) 55 { 56 if(e[i][k]>inff) 57 continue; 58 for(int j=1;j<i;j++) 59 { 60 if(e[i][j]>e[i][k]+e[k][j]) 61 { 62 ans+=k; 63 ans-=a[i][j]; 64 a[i][j]=a[j][i]=k; 65 e[i][j]=e[j][i]=e[i][k]+e[k][j]; 66 } 67 } 68 } 69 } 70 //i:4 j:2 k:1 71 //i:3 j:1 k:2 72 ll w=ans*2%mod; 73 cout<<w<<endl; 74 } 75 return 0; 76 }
小知识点回顾:
- 0x3f3f3f3f3f3f3f3f 代表针对longlong表示的无穷大,约19位
- dijkstra:时间复杂度O(n2),一般求单源最短路
针对无负权边的图,有向无向都可以
存储结构上比较占空间,因为用的是邻接矩阵。
- floyd:适用于N比较小的情况。
注意模板上k是写在最外层循环。
for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { e[i][j]=min(e[i][j],e[i][k]+e[k][j]); } } }
- SPFA:不能处理负权回路;
- 最短路在处理初始边的时候记得判断是否是双向边,是否需要取最小值;
- 问起点到终点距离问题的时候,有时需要考虑一下起点是不是就是终点;
- 记得对边进行初始化
TO DO LIST
- w 50+
- 复习了最短路floyd/dijkstra
待解决问题:
- 为什么floyd的含k循环写在最外层? --> 枚举的松弛点
- HDU6714 代码理解的不是很透彻,不确定自己理解的是否正确,需要推理出结果。???