图论---------最短路专题

在学习以下算法之前必须要有很好的建图基础。

存图一般用

  邻接矩阵  

  邻接表   

  前向星/链式前向星  

  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

 

推荐题目:
P2910  P2017  hdu 2544

  这个算法尽管很容易理解,但是复杂度过高,很多问题都难以解决。下面推出Dijkstra。

 

二. Dijkstra

  时间复杂度 O(n*m)

  缺点:不能处理负权边

推荐题目:

hdu 2066

  

 

三.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 */ 
Bellman-Ford

 

题目推荐:

   poj 3259

四.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 } 
POJ3259 AC代码

 

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 }
P1144 最短路计数

 

 

五.堆优化后的 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 }
P4779 堆优化的Dijkstra

 

 六.A*算法

   可以轻松求到第k短路

 

posted @ 2019-10-21 20:51  生活待我如初恋  阅读(144)  评论(0编辑  收藏  举报