图论---------最短路专题
在学习以下算法之前必须要有很好的建图基础。
存图一般用
邻接矩阵
邻接表
前向星/链式前向星
vector模拟邻接矩阵
知道以上存图工具,下来正式开始码算法了
一.Floyd
时间复杂度O(n的三次方),容易理解
Floyd是一个最入门的最短路的求值过程,思想有点偏向dp。
1 int mp[110][110];// 例如;mp[x][y]存的是:x结点到y结点的最短路值为mp[x][y] 2 int main(){ 3 /* 4 算法的思想: 5 先进行初始化,对于i==j的情况,则mp[i][j]=0; 6 否则 mp[i][j]=inf (这里的inf为无穷大) 7 然后再寻 i到j的路中是否可以经过中间结点k,使得i到j的距离更短 8 即:mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]) 9 经过三重循环就能得到最优解 10 */
算法实现:
1 #include<iostream> 2 using namespace std; 3 int n,m,mp[110][110]; 4 const int inf=9999999; 5 void init(){ 6 cin>>n>>m;//n个点 m条边 7 for (int i=1;i<=n;i++){ 8 for (int j=1;j<=n;j++){ 9 mp[i][j]=(i==j?0:inf); 10 } 11 } 12 int u,v,w; //结点u到结点v的权值为w (这里为有向边) 13 for (int i=1;i<=m;i++){ 14 cin>>u>>v>>w; 15 mp[u][v]=min(mp[u][v],w); 16 } 17 } 18 void Floyd(){ //算法实现 19 for (int k=1;k<=n;k++){ 20 for (int i=1;i<=n;i++){ 21 for (int j=1;j<=n;j++){ 22 mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]); 23 } 24 } 25 } 26 } 27 int main(){ 28 init(); 29 Floyd(); 30 int u,v; 31 while (cin>>u>>v){ 32 printf("结点%d到结点%d的最短距离为%d\n",u,v,mp[u][v]); 33 } 34 return 0; 35 }
自测数据:
4 8 1 2 2 1 3 6 1 4 4 2 3 3 3 1 7 3 4 1 4 1 5 4 3 12
1 2 结点1到结点2的最短距离为2 1 3 结点1到结点3的最短距离为5 1 4 结点1到结点4的最短距离为4 2 3 结点2到结点3的最短距离为3 2 4 结点2到结点4的最短距离为4 3 4 结点3到结点4的最短距离为1 4 3 结点4到结点3的最短距离为10
这个算法尽管很容易理解,但是复杂度过高,很多问题都难以解决。下面推出Dijkstra。
二. Dijkstra
时间复杂度 O(n*m)
缺点:不能处理负权边
推荐题目:
三.Bellman-Ford
复杂度O(n*m)
建立一个dis[]数组,例如:dis【n】记录的是 源点到结点n的最短距离
1 /* 2 算法伪码: 3 dis[len]; 4 while (一般跳出条件为dis不能再次更新的时候){ //当出现负环的时候就成死循环了,看题目具体而议 5 6 for (一个顶点编号--->最后一个结点编号){ 7 遍历该结点的所有边 8 例如:该结点编号为u,有一个通向结点v的有向边权值为w 9 执行: 10 dis[v]=min(dis[v],dis[u]+w); 11 } 12 } 13 */
题目推荐:
四.SPFA
时间复杂度O(nm)
就是经过队列优化的Bellman-Ford
优化操作:将顶点存入队列中(存入的时候记得要标记),然后依次取出,看是否符合更新边的条件。
贴一题:POJ:3259
1 /* 2 题意: 3 测试样例的第一行: 4 n,m,w 分别代表结点数,权值为正的双向边数, 权值为负的单向边边数 5 下面接着m行: 6 每行三个整数:u,v,w 结点u与结点v的双向边权值为w 7 下面接着w行: 8 每行三个整数:u,v,w 结点u到结点v的权值为 -w 9 10 翻译过来就是要求 11 如果存在dis【n】为负数的情况输出YES 12 否则输出NO 13 */
1 /* 2 思路: 3 其实就是判定存不存在负环 4 5 解决方法: 6 如果存在负环,那么肯定有一个结点n可以无限更新dis[n]值 7 当更新次数为>=n的时候,就一定存在负环,所以循环在这里跳出 8 */
1 #include<iostream> 2 #include<vector> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 const int MAXN=550,MAXM=6010,inf=99999999; 7 int n,m,w,dis[MAXN],cnt[MAXN]; 8 struct Edge{ 9 int u,v,w; 10 }e[MAXM]; 11 bool vis[MAXN]; 12 vector<Edge>vec[MAXN]; 13 void init(){ 14 cin>>n>>m>>w; 15 for (int i=0;i<=n;i++){ 16 vec[i].clear(); 17 dis[i]=inf; 18 cnt[i]=0; 19 } 20 memset(vis,false,sizeof(vis)); 21 Edge tmp; 22 for (int i=1;i<=m;i++){ 23 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 24 vec[e[i].u].push_back(e[i]); 25 tmp.u=e[i].v; 26 tmp.v=e[i].u; 27 tmp.w=e[i].w; 28 vec[e[i].v].push_back(tmp); 29 } 30 for (int i=1;i<=w;i++){ 31 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 32 tmp.u= e[i].u; 33 tmp.v= e[i].v; 34 tmp.w=-e[i].w; 35 vec[e[i].u].push_back(tmp); 36 } 37 return ; 38 } 39 40 bool spfa(int u){ 41 dis[u]=0; 42 queue<int>q; 43 while (!q.empty()) 44 q.pop(); 45 q.push(u); 46 while (!q.empty()){ 47 int s=q.front(); 48 q.pop(); 49 if (cnt[s]>=n) 50 return true; 51 for (int i=0;i<vec[s].size();i++){ 52 if (vis[vec[s][i].v]==false||dis[vec[s][i].v]>dis[s]+vec[s][i].w){ 53 vis[vec[s][i].v]=true; 54 if (dis[vec[s][i].v]>dis[s]+vec[s][i].w){ 55 dis[vec[s][i].v]=dis[s]+vec[s][i].w; 56 cnt[vec[s][i].v]++; 57 if (cnt[vec[s][i].v]>=n) 58 return true; 59 q.push(vec[s][i].v); 60 vis[s]=vis[vec[s][i].v]=false; 61 } 62 } 63 } 64 } 65 return false; 66 } 67 int main(){ 68 69 int F; 70 scanf("%d",&F); 71 while (F--){ 72 init(); 73 if (spfa(1)) 74 printf("YES\n"); 75 else 76 printf("NO\n"); 77 } 78 return 0; 79 }
P1144
1 #include<iostream> 2 #include<vector> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 const int MAXN=1e6+3,Mod=100003; 7 int n,m,ans[MAXN],cnt[MAXN]; 8 vector<int>vec[MAXN]; 9 void BFS(){ 10 ans[1]=0; 11 cnt[1]=1; 12 queue<int>q; 13 while(!q.empty()) 14 q.pop(); 15 q.push(1); 16 while (!q.empty()){ 17 int res=q.front(); 18 q.pop(); 19 for (int i=0;i<vec[res].size();i++){ 20 if (vec[res][i]!=1&&(ans[vec[res][i]]==0||ans[vec[res][i]]==ans[res]+1)){ 21 if (ans[vec[res][i]]==0) 22 q.push(vec[res][i]); 23 cnt[vec[res][i]]=(cnt[vec[res][i]]+cnt[res])%Mod; 24 ans[vec[res][i]]=ans[res]+1; 25 } 26 } 27 } 28 return ; 29 } 30 int main() 31 { 32 int u,v; 33 scanf("%d%d",&n,&m); 34 for(int i=1;i<=m;i++){ 35 scanf("%d%d",&u,&v); 36 vec[u].push_back(v); 37 vec[v].push_back(u); 38 } 39 BFS(); 40 for (int i=1;i<=n;i++) 41 printf("%d\n",cnt[i]%Mod); 42 return 0; 43 }
五.堆优化后的 Dijkstra
时间复杂度:O((n+m)logn)
有些题目不存在负权边,但是出的数据会卡SPFA,这个时候又要回到Dijkstra
P4779
1 #include<iostream> 2 #include<bits/stdc++.h> 3 using namespace std; 4 5 const int MAXN=1e5+10,MAXM=2e5+10,inf=0x7fffffff; 6 struct Edge{ 7 int to,w,next; 8 }e[MAXM]; 9 int cnt=0,head[MAXN],dis[MAXN]; 10 bool vis[MAXN]; 11 void Add(int u,int v,int w){ 12 cnt++; 13 e[cnt].to=v; 14 e[cnt].w=w; 15 e[cnt].next=head[u]; 16 head[u]=cnt; 17 } 18 struct Node{ 19 int u,dis; 20 bool operator <(const Node &x) const 21 { 22 return x.dis<dis; 23 } 24 }; 25 std::priority_queue<Node> q; 26 void dijkstra(int start){ 27 dis[start]=0; 28 q.push((Node){start,0}); 29 while(!q.empty()){ 30 Node tmp=q.top(); 31 q.pop(); 32 int x=tmp.u,d=tmp.dis; 33 if (vis[x]) 34 continue; 35 vis[x]=true; 36 for (int i=head[x];i;i=e[i].next){ 37 int to=e[i].to; 38 if (dis[to]>dis[x]+e[i].w){ 39 dis[to]=dis[x]+e[i].w; 40 if (!vis[to]){ 41 q.push((Node){to,dis[to]}); 42 } 43 } 44 } 45 } 46 } 47 int main(){ 48 49 int n,m,start; 50 scanf("%d%d%d",&n,&m,&start); 51 for (int i=1;i<=n;i++) 52 dis[i]=inf; 53 int u,v,w; 54 for (int i=1;i<=m;i++){ 55 scanf("%d%d%d",&u,&v,&w); 56 Add(u,v,w); 57 } 58 dijkstra(start); 59 for (int i=1;i<=n;i++){ 60 cout<<dis[i]<<" "; 61 } 62 cout<<endl; 63 return 0; 64 }
六.A*算法
可以轻松求到第k短路