觉得一篇讲SPFA还不错的文章
我觉得他整理的有一些乱,我都改成插入代码了,看的顺眼一些
转载自http://blog.csdn.net/juststeps/article/details/8772755
下面的都是原文:
最短路径 之 SPFA算法
http://hi.baidu.com/southhill/item/ab26a342590a5aae60d7b967
求最短路径的算法有许多种,除了排序外,恐怕是OI界中解决同一类问题算法最多的了。最熟悉的无疑是Dijkstra,接着是Bellman-Ford,它们都可以求出由一个源点向其他各点的最短路径;如果我们想要求出每一对顶点之间的最短路径的话,还可以用Floyd-Warshall。
SPFA是这篇日志要写的一种算法,它的性能非常好,代码实现也并不复杂。特别是当图的规模大,用邻接矩阵存不下的时候,用SPFA则可以很方便地面对临接表。每个人都写过广搜,SPFA的实现和广搜非常相似。
如何求得最短路径的长度值?
首先说明,SPFA是一种单源最短路径算法,所以以下所说的“某点的最短路径长度”,指的是“某点到源点的最短路径长度”。
我们记源点为S,由源点到达点i的“当前最短路径”为D[i],开始时将所有D[i]初始化为无穷大,D[S]则初始化为0。算法所要做的,就是在运行过程中,不断尝试减小D[]数组的元素,最终将其中每一个元素减小到实际的最短路径。
过程中,我们要维护一个队列,开始时将源点置于队首,然后反复进行这样的操作,直到队列为空:
(1)从队首取出一个结点u,扫描所有由u结点可以一步到达的结点,具体的扫描过程,随存储方式的不同而不同;
(2)一旦发现有这样一个结点,记为v,满足D[v] > D[u] + w(u, v),则将D[v]的值减小,减小到和D[u] + w(u, v)相等。其中,w(u, v)为图中的边u-v的长度,由于u-v必相邻,所以这个长度一定已知(不然我们得到的也不叫一个完整的图);这种操作叫做松弛。
引用内容
松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对i,j进行松弛,就是判定是否d[j]>d[i]+w[i,j],如果该式成立则将d[j]减小到d[i]+w[i,j],否则不动。
(3)上一步中,我们认为我们“改进了”结点v的最短路径,结点v的当前路径长度D[v]相比于以前减小了一些,于是,与v相连的一些结点的路径长度可能会相应地减小。注意,是可能,而不是一定。但即使如此,我们仍然要将v加入到队列中等待处理,以保证这些结点的路径值在算法结束时被降至最优。当然,如果连接至v的边较多,算法运行中,结点v的路径长度可能会多次被改进,如果我们因此而将v加入队列多次,后续的工作无疑是冗余的。这样,就需要我们维护一个bool数组Inqueue[],来记录每一个结点是否已经在队列中。我们仅将尚未加入队列的点加入队列。
算法能否结束?
对于不存在负权回路的图来说,上述算法是一定会结束的。因为算法在反复优化各个最短路径长度,总有一个时刻会进入“无法再优化”的局面,此时一旦队列读空,算法就结束了。然而,如果图中存在一条权值为负的回路,就糟糕了,算法会在其上反复运行,通过“绕圈”来无休止地试图减小某些相关点的最短路径值。假如我们不能保证图中没有负权回路,一种“结束条件”是必要的。这种结束条件是什么呢?
思考Bellman-Ford算法,它是如何结束的?显然,最朴素的Bellman-Ford算法不管循环过程中发生了什么,一概要循环|V|-1遍才肯结束。凭直觉我们可以感到,SPFA算法“更聪明一些”,就是说我们可以猜测,假如在SPFA中,一个点进入队列——或者说一个点被处理——超过了|V|次,那么就可以断定图中存在负权回路了。
最短路径本身怎么输出?
在一幅图中,我们仅仅知道结点A到结点E的最短路径长度是73,有时候意义不大。这附图如果是地图的模型的话,在算出最短路径长度后,我们总要说明“怎么走”才算真正解决了问题。如何在计算过程中记录下来最短路径是怎么走的,并在最后将它输出呢?
Path[]数组,Path[i]表示从S到i的最短路径中,结点i之前的结点的编号。注意,是“之前”,不是“之后”。最短路径算法的核心思想成为“松弛”,原理是三角形不等式,方法是上文已经提及的。我们只需要在借助结点u对结点v进行松弛的同时,标记下Path[v] = u,记录的工作就完成了。
输出时可能会遇到一点难处,我们记的是每个点“前面的”点是什么,输出却要从最前面往最后面输,这不好办。其实很好办,见如下递归方法:
程序代码
void PrintPath(int k){
if( Path[k] ) PrintPath(Path[k]);
fout<<k<<' ';
}
SPFA的代码怎么写?
我写了邻接表和邻接矩阵两种,两者想像起来是那么的不同,算法的思路上实在区别不大,只是用不同方式诠释“扫描”的过程而已。只给出SPFA的单个函数,我不觉得很容易看懂,但是我仍然把两个程序的SPFA函数放在下面。在日志的结尾处,有一个完整版文件下载。贴程序,首先是邻接表的:
程序代码
void SPFA(){ for(int i=1; i<=gv; i++) Dist[i] = 100000; Dist[S] = 0; int closed = 0, open = 1; queue[1] = S; Inqueue[S] = true; do{ closed++; node *tmp = connect[queue[closed]]; Inqueue[queue[closed]] = false; while(tmp != NULL){ if( Dist[tmp->key] > Dist[queue[closed]] + tmp->w ){ Dist[tmp->key] = Dist[queue[closed]] + tmp->w; Path[tmp->key] = queue[closed]; if( !Inqueue[tmp->key] ){ Inqueue[tmp->key] = true; open++; queue[open] = tmp->key; } } tmp = tmp->next; } }while(closed < open); }
然后是邻接矩阵的:
程序代码
void SPFA(){ for( int i=1; i<=gv; i++){ Dist[i] = 100000; for( int j=1; j<=gv; j++) if( !Graph[i][j] && i!=j) Graph[i][j] = 100000; } int closed = 0, open = 1; queue[1] = S; Dist[S] = 0; do{ closed++; int u = queue[closed]; Inqueue[u] = false; for(int i=1; i<=gv; i++) if ( Dist[i] > Dist[u] + Graph[u][i] ){ Dist[i] = Dist[u] + Graph[u][i]; Path[i] = u; if( !Inqueue[i] ){ Inqueue[i] = true; open++; queue[open] = i; } } }while(closed < open); }
spfa算法 Easy sssp 收藏
输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图.
要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一个点沿着某条路径出发, 又回到了自己, 而且所经过的边上的权和小于0, 就说这条路是一个负权回路.
如果存在负权回路, 只输出一行-1;
如果不存在负权回路, 再求出一个点S(1 <= S <= N)到每个点的最短路的长度. 约定: S到S的距离为0, 如果S与这个点不连通, 则输出NoPath.
INPUT:
第一行: 点数N(2 <= N <= 1,000), 边数M(M <= 100,000), 源点S(1 <= S <= N);
以下M行, 每行三个整数a, b, c表示点a, b(1 <= a, b <= N)之间连有一条边, 权值为c(-1,000,000 <= c <= 1,000,000)
OUTPUT:
如果存在负权环, 只输出一行-1, 否则按以下格式输出
共N行, 第i行描述S点到点i的最短路:
如果S与i不连通, 输出NoPath;
如果i = S, 输出0;
其他情况输出S到i的最短路的长度
INPUT:
6 8 1
1 3 4
1 2 6
3 4 -7
6 4 2
2 4 5
3 6 3
4 5 1
3 5 4
OUTPUT:
0
6
4
-3
-2
7
注意:
题目说的不是很清楚,给出的图不一定是完全联通图,有些是断开的几个图,所以在判断的源点是否有环以外还要分别对不同的点进行spfa呀。再进行分别的判断和输出。
有几个优化:
1.可以先判断是否有负权自环,有则直接输出-1
2.在枚举的过程中,当这个顶点的最短路(d[i])<0时,有负权回路,输出-1.
【参考程序】:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 long queue[1001],a[1001],psum[1001],dis[1001],l[1001][1001],cost[1001][1001]; 5 long n,m,s,i,j; 6 bool hash[1001],bk; 7 void spfa(int s) 8 { 9 int head,tail,start,now,i; 10 for (i=1;i<=n;i++) 11 { 12 dis[i]=0xfffffff; 13 psum[i]=0; 14 hash[i]=false; 15 } 16 head=tail=1;hash[s]=true; 17 psum[s]=1;dis[s]=0;queue[1]=s; 18 while (head<=tail) 19 { 20 start=queue[(head-1)%n+1]; 21 hash[start]=true; 22 for (i=1;i<=l[start][0];i++) 23 { 24 now=l[start][i]; 25 if (dis[now]>dis[start]+cost[start][now]) 26 { 27 dis[now]=dis[start]+cost[start][now]; 28 if (!hash[now]) 29 { 30 hash[now]=true; 31 tail++; 32 queue[(tail-1)%n+1]=now; 33 psum[now]++; 34 if (psum[now]>n) 35 {//记录每个点进队的次数(判断环的关键} 36 bk=false; 37 return; 38 } 39 } 40 } 41 } 42 head++; 43 hash[start]=false; 44 if (dis[s]<0) 45 {//判断环的一个优化 46 bk=false; 47 return; 48 } 49 } 50 } 51 void output() 52 { 53 bk=true; 54 spfa(s); 55 if (!bk) 56 { 57 printf("-1\n"); 58 return; 59 } 60 memcpy(a,dis,sizeof(long)*(n+1)); 61 for (i=1;i<=n;i++) 62 if (a[i]==0xfffffff) 63 { 64 bk=true; 65 spfa(i); 66 if (!bk) 67 { 68 printf("-1\n"); 69 return; 70 } 71 } 72 for (i=1;i<=n;i++) 73 if (a[i]==0xfffffff) printf("NoPath\n"); 74 else printf("%d\n",a[i]); 75 } 76 void input() 77 { 78 scanf("%d%d%d",&n,&m,&s); 79 for (i=1;i<=n;i++) 80 for (j=1;j<=n;j++) 81 if (i==j) cost[i][j]=0; 82 else cost[i][j]=0xfffffff; 83 memset(l,0,sizeof(l)); 84 int x,y,c; 85 for (i=1;i<=m;i++) 86 { 87 scanf("%d%d%d",&x,&y,&c); 88 if (c<cost[x][y]) 89 { 90 cost[x][y]=c; 91 l[x][0]++; 92 l[x][l[x][0]]=y; 93 } 94 } 95 } 96 int main() 97 { 98 input(); 99 output(); 100 system("pause"); 101 return 0; 102 }
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/bobcowwocb/archive/2009/09/14/4550188.aspx
2009年07月24日 星期五 15:10
SPFA算法模版+邻接表实现
SPFA即shotest path faster algorithm,由意思就可以看出该算法效率比较高。
其实SPFA就是bellman-ford算法的一个优化。
具体做法是用一个队列保存待松弛的点,然后对于每个出队的点依次遍历每个与他有边相邻的点(用邻接表效率较高),如果该点可以松弛并且队列中没有该点则将它加入队列中,如此迭代直到队列为空。
据说平均效率是O(E),可见对边稀疏的图用此算法效果是相当可观的。
若要判负环路,则记录一个点的入队次数,若超过边数,则有负权环。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <queue> 3 using namespace std; 4 5 const long MAXN=10000; 6 const long lmax=0x7FFFFFFF; 7 8 typedef struct 9 { 10 long v; 11 long next; 12 long cost; 13 }Edge; 14 15 16 Edge e[MAXN]; 17 long p[MAXN]; 18 long Dis[MAXN]; 19 bool vist[MAXN]; 20 21 queue<long> q; 22 23 long m,n;//点,边 24 void init() 25 { 26 long i; 27 long eid=0; 28 29 memset(vist,0,sizeof(vist)); 30 memset(p,-1,sizeof(p)); 31 fill(Dis,Dis+MAXN,lmax); 32 33 while (!q.empty()) 34 { 35 q.pop(); 36 } 37 38 for (i=0;i<n;++i) 39 { 40 long from,to,cost; 41 scanf("%ld %ld %ld",&from,&to,&cost); 42 43 e[eid].next=p[from]; 44 e[eid].v=to; 45 e[eid].cost=cost; 46 p[from]=eid++; 47 48 //以下适用于无向图 49 swap(from,to); 50 51 e[eid].next=p[from]; 52 e[eid].v=to; 53 e[eid].cost=cost; 54 p[from]=eid++; 55 56 } 57 } 58 59 void print(long End) 60 { 61 //若为lmax 则不可达 62 printf("%ld\n",Dis[End]); 63 } 64 65 void SPF() 66 { 67 68 init(); 69 70 long Start,End; 71 scanf("%ld %ld",&Start,&End); 72 Dis[Start]=0; 73 vist[Start]=true; 74 q.push(Start); 75 76 while (!q.empty()) 77 { 78 long t=q.front(); 79 q.pop(); 80 vist[t]=false; 81 long j; 82 for (j=p[t];j!=-1;j=e[j].next) 83 { 84 long w=e[j].cost; 85 if (w+Dis[t]<Dis[e[j].v]) 86 { 87 Dis[e[j].v]=w+Dis[t]; 88 if (!vist[e[j].v]) 89 { 90 vist[e[j].v]=true; 91 q.push(e[j].v); 92 } 93 } 94 } 95 } 96 97 print(End); 98 99 } 100 101 int main() 102 { 103 while (scanf("%ld %ld",&m,&n)!=EOF) 104 { 105 SPF(); 106 } 107 return 0; 108 }
一、Bellman-Ford算法
最优性原理
它是最优性原理的直接应用,算法基于以下事实:
l 如果最短路存在,则每个顶点最多经过一次,因此不超过n-1条边;
l 长度为k的路由长度为k-1的路加一条边得到;
l 由最优性原理,只需依次考虑长度为1,2,…,k-1的最短路。
适用条件&范围
l 单源最短路径(从源点s到其它所有顶点v);
l 有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);
l 边权可正可负(如有负权回路输出错误提示);
l 差分约束系统(需要首先构造约束图,构造不等式时>=表示求最小值, 作为最长路,<=表示求最大值, 作为最短路。<=构图时, 有负环说明无解;求不出最短路(为Inf)为任意解。>=构图时类似)。
算法描述
l 对每条边进行|V|-1次Relax操作;
l 如果存在(u,v)∈E使得dis[u]+w<dis[v],则存在负权回路;否则dis[v]即为s到v的最短距离,pre[v]为前驱。
时空复杂度
for i:=1 to |V|-1 do
for 每条边(u,v)∈E do Relax(u,v,w);
for每条边(u,v)∈E do
if dis[u]+w<dis[v] Then Exit(False)
算法时间复杂度O(VE)。因为算法简单,适用范围又广,虽然复杂度稍高,仍不失为一个很实用的算法。
改进和优化 如果循环n-1次以前已经发现不存在紧边则可以立即终止; Yen氏改进(不降低渐进复杂度);SPFA算法
二、 SPFA算法
算法简介
SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。 它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。
算法流程
SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的 改变。因此,算法大致流程是用一个队列来进行维护,即用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。当队列不为空时,取出队首顶点, 对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实 现,需要用到一个先进先出的队列 queue 和一个指示顶点是否在队列中的标记数组mark。为了方便查找某个顶点的邻接点,图采用临界表存储。
算法代码
Procedure SPFA;Begin initialize-single-source(G,s); initialize-queue(Q); enqueue(Q,s); while not empty(Q) do begin u:=dequeue(Q); for each v∈adj[u] do begin tmp:=d[v]; relax(u,v); if (tmp<>d[v]) and (not v in Q) then enqueue(v); end; end;End;负环处理
需要特别注意的是:仅当图不存在负权回路时,SPFA能正常工作。如果图存在负权回路,由于负权回路上的顶点无法收敛,总有顶点在入队和出队往返,队列无法为空,这种情况下SPFA无法正常结束。
判断负权回路的方案很多,世间流传最广、比较容易实现并且高效的方法的是记录每个结点进队次数,超过|V|次表示有负权。
三、 学以致用
POJ 1201 Intervals 差分约束系统
设S(i)为 0..i-1 中在最终序列中的的整数个数。则约束条件如下:
S(b)-S(a) >= c
0 <= S(i+1) - S(i) <= 1 <==> S(i+1)-S(i) >= 0;
S(i)-S(i+1) >= -1
注意本题要求的是最小值, 而按照>=号建图后发现图中有负环, 怎么办呢?
其实很简单, 本题求的不是最短路, 而是最长路! Bellman_ford即可!
POJ 1275 Cashier Employment 出纳员的雇佣
黑书上有详细讲解
POJ 1364 King 差分约束系统
这个题目构图之后, 只需要用bellman_ford判断是否有负圈.
构图方法:
首先进行转换:a[j]+...+a[j+m] = a[1]+...a[j+m] - (a[1]+...+a[j-1]) = sum[j+m] -
sum[j-1] >(<) ki. 差分约束只能全部是<=或者(>=).
第二步转换: sum[j+m]-sum[j-1] <= ki-1 或者 sum[j-1]-sum[j+m] <= -ki-1.
约束图构造好后就是简单的Bellman-Ford了!
POJ 1716 Integer Intervals 是1201的简单版本, 贪心算法能够得到更好的效果.
POJ 2983 Is the Information Reliable?
差分约束题, 处理一下等号的情况, 然后普通的Bellman_ford
POJ 3159 Candies 最短路径
Bellman-Ford超时, Dijkstra算法可以高效解决, SPFA(队列)居然超时...SPFA修改为堆栈实现就过了.
POJ 3169 Layout 差分约束
Bellman-Ford 和 SPFA 实现均可
POJ 3259 Wormholes 判断负权回路
TOJ 2976 Path 单纯的最短路径 可练习SPFA
ZOJ 3033 Board Games 我做的第一道Bellman-Ford题目
首先,DFS判断可达性,不可达直接输出infinity结束,可达,bellman-ford判断是否存在负环,存在输出infinity,否则,输出最短距离。
SPFA算法模版+邻接表实现
SPFA即shotest path faster algorithm,由意思就可以看出该算法效率比较高。
其实SPFA就是bellman-ford算法的一个优化。
具体做法是用一个队列保存待松弛的点,然后对于每个出队的点依次遍历每个与他有边相邻的点(用邻接表效率较高),如果该点可以松弛并且队列中没有该点则将它加入队列中,如此迭代直到队列为空。
据说平均效率是O(E),可见对边稀疏的图用此算法效果是相当可观的。
若要判负环路,则记录一个点的入队次数,若超过边数,则有负权环。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <queue> 3 using namespace std; 4 5 const long MAXN=10000; 6 const long lmax=0x7FFFFFFF; 7 8 typedef struct 9 { 10 long v; 11 long next; 12 long cost; 13 }Edge; 14 15 16 Edge e[MAXN]; 17 long p[MAXN]; 18 long Dis[MAXN]; 19 bool vist[MAXN]; 20 21 queue<long> q; 22 23 long m,n;//点,边 24 void init() 25 { 26 long i; 27 long eid=0; 28 29 memset(vist,0,sizeof(vist)); 30 memset(p,-1,sizeof(p)); 31 fill(Dis,Dis+MAXN,lmax); 32 33 while (!q.empty()) 34 { 35 q.pop(); 36 } 37 38 for (i=0;i<n;++i) 39 { 40 long from,to,cost; 41 scanf("%ld %ld %ld",&from,&to,&cost); 42 43 e[eid].next=p[from]; 44 e[eid].v=to; 45 e[eid].cost=cost; 46 p[from]=eid++; 47 48 //以下适用于无向图 49 swap(from,to); 50 51 e[eid].next=p[from]; 52 e[eid].v=to; 53 e[eid].cost=cost; 54 p[from]=eid++; 55 56 } 57 } 58 59 void print(long End) 60 { 61 //若为lmax 则不可达 62 printf("%ld\n",Dis[End]); 63 } 64 65 void SPF() 66 { 67 68 init(); 69 70 long Start,End; 71 scanf("%ld %ld",&Start,&End); 72 Dis[Start]=0; 73 vist[Start]=true; 74 q.push(Start); 75 76 while (!q.empty()) 77 { 78 long t=q.front(); 79 q.pop(); 80 vist[t]=false; 81 long j; 82 for (j=p[t];j!=-1;j=e[j].next) 83 { 84 long w=e[j].cost; 85 if (w+Dis[t]<Dis[e[j].v]) 86 { 87 Dis[e[j].v]=w+Dis[t]; 88 if (!vist[e[j].v]) 89 { 90 vist[e[j].v]=true; 91 q.push(e[j].v); 92 } 93 } 94 } 95 } 96 97 print(End); 98 99 } 100 101 int main() 102 { 103 while (scanf("%ld %ld",&m,&n)!=EOF) 104 { 105 SPF(); 106 } 107 return 0; 108 }
POJ 1511-Invitation Cards(SPFA算法)
今天终于用SPFA写出了第一个程序,感觉收获很大,从Dij到Floyed再到Bellmen,以及今天的SPFA,每一种算法背后都蕴藏着许多值得思考的地方。正因为研究了它们,才使得我的能力不断地获得了提高。
之前以为SPFA做为最短路问题最快的算法,想必代码定不好写,不过今天研究过才知道,SPFA的代码量远远不及Dij,这着实令人惊叹,原来最好的算法SPFA是如此的好写,呵呵 我想此算法在很大程度上可以完全代替之前的算法,以后再碰到最短路问题时,SPFA一定能成为首要的选择!
PS:由于是用邻接表来存储的,所以每次操作前要收回以前分配的内存,我尝试了收回和不收回两种方法,发现其实差别不大,如果纯粹是比赛的话,可能不收回反而会更好些(避免超时)。当然如果在实际应用中,应该切记内存的分配,否则软件可能会发生异常。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //Coded by abilitytao 2 //Time:2009-04-10 22:49:58 3 #include<iostream> 4 #include<cmath> 5 #include<queue> 6 using namespace std; 7 #define MAX_NUM 1000000001 8 #define MAX_DOTNUM 1000001 9 10 int n,m; 11 queue<int>myqueue; 12 bool mark[MAX_DOTNUM]; 13 __int64 dis[MAX_DOTNUM]; 14 15 16 struct node 17 { 18 19 int v; 20 int w; 21 node *next; 22 }edge[MAX_DOTNUM];//此邻接表用于存储正向图 23 24 node reversed_edge[MAX_DOTNUM];//此逆邻接表用于存储逆向图 25 26 void initial(node edge[])//邻接表的初始化,里面封装了回收上一次操作所分配之内存的操作 27 { 28 int i; 29 node *p; 30 node *q; 31 for(i=1;i<=n;i++) 32 { 33 p=&edge[i]; 34 q=p->next; 35 while(q!=NULL) 36 { 37 p->next=q->next; 38 delete q; 39 q=p->next; 40 } 41 } 42 } 43 44 45 void input_case()//每一个case的输入函数 46 { 47 48 int i; 49 for(i=1;i<=m;i++) 50 { 51 node *p; 52 node *q; 53 int a,b,c; 54 scanf("%d%d%d",&a,&b,&c); 55 /**///////////////////////// 56 p=&edge[a]; 57 q=new node; 58 q->v=b; 59 q->w=c; 60 q->next=p->next; 61 p->next=q; 62 /**///////////////////////// 63 p=&reversed_edge[b]; 64 q=new node; 65 q->v=a; 66 q->w=c; 67 q->next=p->next; 68 p->next=q; 69 } 70 } 71 72 73 void spfa(node edge[])//SPFA部分 74 { 75 76 int i; 77 /**//////////////////////////////////////////////////////////////// 78 memset(mark,false,sizeof(mark)); 79 for(i=1;i<=n;i++) 80 dis[i]=MAX_NUM; 81 while(myqueue.size()!=0) 82 myqueue.pop(); 83 /**//////////////////////////////////////////////////////////// 84 dis[1]=0; 85 mark[1]=true; 86 myqueue.push(1); 87 while(myqueue.size()!=0)//如果队列不空,则进行松弛操作,直到队列空为止 88 { 89 int temp=myqueue.front(); 90 myqueue.pop(); 91 mark[temp]=false; 92 node *p; 93 for(p=edge[temp].next;p!=NULL;p=p->next) 94 { 95 if(dis[p->v]>dis[temp]+p->w) 96 { 97 dis[p->v]=dis[temp]+p->w; 98 if(mark[p->v]!=true) 99 { 100 myqueue.push(p->v); 101 mark[p->v]=true; 102 } 103 } 104 } 105 } 106 } 107 108 109 int main() 110 { 111 112 int testcase; 113 int i,j; 114 __int64 sum; 115 scanf("%d",&testcase); 116 for(i=1;i<=MAX_DOTNUM-1;i++) 117 { 118 edge[i].v=i; 119 edge[i].w=0; 120 edge[i].next=NULL; 121 } 122 for(i=1;i<=MAX_DOTNUM-1;i++) 123 { 124 reversed_edge[i].v=i; 125 reversed_edge[i].w=0; 126 reversed_edge[i].next=NULL; 127 } 128 for(i=1;i<=testcase;i++) 129 { 130 sum=0; 131 scanf("%d%d",&n,&m); 132 initial(edge); 133 initial(reversed_edge); 134 input_case(); 135 spfa(edge); 136 for(j=1;j<=n;j++) 137 sum+=dis[j]; 138 spfa(reversed_edge); 139 for(j=1;j<=n;j++) 140 sum+=dis[j]; 141 printf("%I64d\n",sum); 142 } 143 system("pause"); 144 return 0; 145 146 }