最短路径 Floyd && spfa

floyd算法是一个很强大的算法,它可以计算任意两点之间的最短路径,其边可以为负值。
时间复杂度n^3

 1 void floyd()
 2 {
 3     int k,u,v;
 4     for(k=0;k<G.vunm;k++)
 5         for(u=0;u<G.vunm;u++)
 6             for(v=0;v<G.vunm;v++)
 7             {
 8                 if(D[u][v]>D[u][k]+D[k][v])
 9                 {
10                     D[u][v]=D[u][k]+D[k][v];
11                 }
12             }
13     printf("%d
",D[s][t]==MAX?-1:D[s][t]);
14 }
Floyd 模板

补充一下:对于floyd判断负环是否存在只需检查是否存在d[i][i]是负数的顶点i 即可

传送门: http://poj.org/problem?id=3259

SPFA算法 :设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。

SPFA 是这样判断负环的: 如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #define MAX 999999999
 6 
 7 using namespace std;
 8 
 9 int G[503][503];
10 int n; //n为点数
11  
12 bool SPFA()
13 {
14     int u,i;
15     queue<int>que;
16     int dis[503];//存最短距离
17     bool vis[503];//是否访问
18     int flag[503];//判断是否为负环
19     
20     memset(flag,0,sizeof(flag));
21     memset(vis,false,sizeof(vis)) ;
22     fill(dis,dis+n+1,MAX);
23     dis[1]=0;
24     que.push(1);
25     while(!que.empty())
26     {
27         u=que.front();
28         que.pop();    vis[u]=false;
29         for(i=1;i<=n;i++)    //对每个顶点更新相当于BF中通过每个边更新一样 
30         {
31             if(dis[i]>dis[u]+G[u][i])
32             {
33                 dis[i]=dis[u]+G[u][i];
34                 if(!vis[i])//可能多次进入。如果之前进入,则不用再进,因为不用进入也可以i的最短路径值,如果在进入,就累赘了。 
35                 {
36                     vis[i]=true;    
37                     flag[i]++;
38                     if(flag[i]>=n)  //表示存在负环; 
39                         return true;
40                     que.push(i);
41                 }
42             }
43         }
44     }
45     return false;
46 }
47 
48 int main()
49 {
50     int t,k,i,j,u,v,w,m;
51     
52     scanf("%d",&t);
53     while(t--)
54     {
55         scanf("%d%d%d",&n,&m,&k);
56         for(i=1;i<=n;i++)
57         for(j=1;j<=n;j++)
58             G[i][j]=i==j?0:MAX;  //i=j表示值0 否则为max 
59         for(i=0;i<m;i++)
60         {
61             scanf("%d%d%d",&u,&v,&w);
62             G[u][v]=G[v][u]=w;
63         }
64         for(i=0;i<k;i++)
65         {
66             scanf("%d%d%d",&u,&v,&w);
67             G[u][v]=-w;  //一定加符号,负权 
68         } 
69         printf("%s
",SPFA()?"YES":"NO");
70     }
71     return 0;
72 }
73 
74  
SPFA模板

SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾

SPFA算法对于稀疏图才能发挥它的大作用,对于稀疏图我们用到的数据结构为  前向星

下面就是 SPFA+前向星的程序  并应用了SLF  双向队列进行优化

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #define MAX 9999999
 6 
 7 using namespace std;
 8 
 9 struct node {
10     int v,w;//v终点,w权值 
11     int nest;//下一个 
12 }; 
13 node edge[5203];//前向星  每个元素即每条边
14 int head[503];//头指针式的数组 即邻接链表的指针数组
15 int cnt;//下标
16 int n;//点的个数
17 
18 void add(int u,int v,int w)//加边  
19 {
20     edge[cnt].v=v;
21     edge[cnt].w=w;
22     edge[cnt].nest=head[u];//相当于链表头插入法 
23     head[u]=cnt++;    // 
24 } 
25 
26 bool SPFA()
27 {
28     int i,u,v;//u从Q中取出的点  v找到的点
29     int dis[503];//保存每点最短距离 
30     int flag[503];//保存某点加入队列的次数
31     bool vis[503];//标记数组
32     deque<int> que;//双向队列
33     
34     fill(dis,dis+n+1,MAX);
35     memset(flag,0,sizeof(flag));
36     memset(vis,false,sizeof(vis));
37     dis[1]=0;//s为1  即1为起点 
38     que.push_back(1);//将1加入队列
39     while(!que.empty()) //队列不为空 
40     {
41         u=que.front();//队列中取出
42         que.pop_front();//删除 
43         vis[u]=false;//标记为未访问
44         for(i=head[u];i!=-1;i=edge[i].nest) //对所有与该点相邻的边进行查找 
45         {
46             v=edge[i].v;
47             if(dis[v]>dis[u]+edge[i].w)
48             {
49                 dis[v]=dis[u]+edge[i].w;//松弛成功
50                 if(!vis[v])// 表示未标记
51                 {
52                     vis[v]=true;//标记
53                     flag[v]++;//表示该点进入队列的次数 
54                     if(flag[v]>=n)//若该点进入队列次数超过n次 说明有负环
55                         return true;//返回有负环 
56                     //以下为SLF优化
57                     if(!que.empty()&&dis[v]<dis[que.front()]) //若为队列为空&&队列队首元素距离大于当前点的距离
58                         que.push_front(v);//加入到队首
59                     else
60                         que.push_back(v); 
61                  } 
62              }     
63         }    
64     }
65     return false;//没有负环 
66 }
67 
68 int main()
69 {
70     int u,v,w,m,k,t;
71     
72     scanf("%d",&t);
73     while(t--)
74     {
75         memset(head,-1,sizeof(head));
76         cnt=0;
77         scanf("%d%d%d",&n,&m,&k);
78         while(m--)
79         {
80             scanf("%d%d%d",&u,&v,&w);
81             add(u,v,w);    add(v,u,w); //双向无向图 
82         }
83         while(k--)
84         {
85             scanf("%d%d%d",&u,&v,&w);
86             add(u,v,-w);
87         }
88         printf("%s
",SPFA()?"YES":"NO");
89     }
90     return 0; 
91 }
92  
SPFA优化
原文地址:https://www.cnblogs.com/WDKER/p/5183771.html