SPFA(Shortest Path Faster Algorithm)算法是西南交通大学的段凡丁于1994提出,是队列优化版的Bellman-Ford。将源点加入队列;每次从队列出来一个点,对相邻的点,进行松弛操作;被松弛成功且不在队列的点依次进入队列;重复操作,直至队列为空算法结束。
dis[u]表示源点到点u的最短距离;用数组queue[ ]模拟队列,head, rear分别表示队首、队尾,dequeue表示出队的元素;within[u]标记节点u在队列中。
算法流程:
(1)初始化:将queue元素全部置为-1,源点进入队列queue。
(2)队首出队,dequeue=queue[head]; head++; 松弛与dequeue相邻的节点v:dis[v]=min{dis[v], dis[dequeue]+(dequeue,v)};
若dis[v]<dis[dequeue]+(dequeue,v),即标明松弛成功,v如果不在队列中则入队;将dequeue标记为已出队。
(3)判断队列为空:dequeue==-1,若不为空,重复操作(2)。
用邻接表(adjacency list)作为图的存储结构,SPFA算法时间复杂度为O(ke)。
在邻接表中,对每个顶点u建立一个单链表。单链表由c(u)个表结点组成,c(u)表示节点u的出度;每一个单链表都有一个表头结点。
1511 | Accepted | 109912K | 6563MS | C | 2651B | 2013-08-16 16:33:55 |
#include "stdio.h" #include "stdlib.h" #include "string.h" #define MAXN 1000000 #define inf 1100000000 __int64 sum; /*表结点*/ typedef struct ArcNode { int adjvex, weight; struct ArcNode *next; }ArcNode; ArcNode *pnew, *rpnew; /*头结点*/ typedef struct VNode { int vertex; struct ArcNode *firstarc; }VNode; /*graph represented by adjacency list*/ typedef struct { int n,m; VNode GVertex[MAXN]; }ALGraph; /*create a graph & a reverse graph by using adjacency-list*/ void CreateGraph(ALGraph *graph,ALGraph *reverse) { int i,n,m; int start,end,wei; scanf("%d%d",&n,&m); graph->n=reverse->n=n; graph->m=reverse->m=m; for(i=0;i<n;i++) { graph->GVertex[i].vertex=i; graph->GVertex[i].firstarc=NULL; reverse->GVertex[i].vertex=i; reverse->GVertex[i].firstarc=NULL; } for(i=0;i<m;i++) { scanf("%d%d%d",&start,&end,&wei); pnew=(ArcNode *) malloc(sizeof(ArcNode)); pnew->adjvex=end-1; pnew->weight=wei; pnew->next=graph->GVertex[start-1].firstarc; graph->GVertex[start-1].firstarc=pnew; rpnew=(ArcNode *) malloc(sizeof(ArcNode)); rpnew->adjvex=start-1; rpnew->weight=wei; rpnew->next=reverse->GVertex[end-1].firstarc; reverse->GVertex[end-1].firstarc=rpnew; } } /*spfa algorithm, the source node is 0*/ void spfa(ALGraph *graph) { const int num_ver=graph->n; int i; int head,rear,dequeue; int *within=(int *) malloc(num_ver*sizeof(int)); int *queue=(int *) malloc((num_ver+1)*sizeof(int)); __int64 *dis=(__int64 *)malloc(num_ver*sizeof(__int64)); /*initialization*/ memset(within,0,num_ver*sizeof(int)); memset(queue,-1,(num_ver+1)*sizeof(int)); for(i=0;i<num_ver;i++) dis[i]=inf; dis[0]=0; queue[0]=0; dequeue=head=rear=0; for(;dequeue!=-1;dequeue=queue[head],head++) { ArcNode *p=graph->GVertex[dequeue].firstarc; while(p!=NULL) { if(dis[p->adjvex]>dis[dequeue]+p->weight) { dis[p->adjvex]=dis[dequeue]+p->weight; if(within[p->adjvex]==0) { within[p->adjvex]=1; queue[rear+1]=p->adjvex; rear++; } } p=p->next; } within[dequeue]=0; } /*calculate the sum of the minimum shortest path*/ for(i=0;i<num_ver;i++) sum+=dis[i]; free(within); free(queue); free(dis); } int main() { int test_case; ALGraph *graph=(ALGraph *) malloc(sizeof(ALGraph)); ALGraph *reverse=(ALGraph *) malloc(sizeof(ALGraph)); scanf("%d",&test_case); while(test_case--) { CreateGraph(graph,reverse); sum=0; spfa(graph); spfa(reverse); printf("%I64d\n",sum); } return 0; }
题目大意:有一个R*C(即R row, C coloumn)的网格,网格中每一点(即location)对应一个速度,速度跟该点的height(elevation)相关,且满足:如果从a移到b,speed[b]=speed[a]*2^(elevation[a]-elevation[b]) ;求从左上角移到右下角的最短路径。
(1)Titanium指出这个题的巧妙之处:无论怎么走,从起点走到任何一个点,到达的那个点的速度都是固定的。
对于线路a--->b---->c ,c点速度为 speed[c]=speed[b]*2^(elevation[b]-elevation[c]) =speed[a]*2^(elevation[a]-elevation[c]) 。
(2)从一个位置a移到其邻接位置(adjacent location)的距离是1.0,所花费的时间为1.0/speed[a] 。
(3)因为只能东南西北的移动,所以网格隐含着一个邻接表。
(4)这道题可以用BFS+Priority_queue优化求解,具体请参看popopopolo。
函数pow(,)内的两个参数类型因保持一致,因pow(2,)而Compile Error一次,应改成pow(2.0,) 。
源代码(C++):
3037 | Accepted | 524K | 313MS | C++ | 1787B | 2013-08-16 22:36:29 |
#include <iostream> #include <cmath> #include <queue> using namespace std; #define MAXR 100 #define MAXC 100 #define inf 11000000000 /*describe a location*/ struct location { int x,y; }; /*represent the west, north, east, south*/ const location direction[4]={{-1,0},{0,-1},{1,0},{0,1}}; int init_speed,row,coloumn; double speed[MAXR][MAXC],dis[MAXR][MAXC]; int elevation[MAXR][MAXC],within[MAXR][MAXC]; /*input and get the speed at each location*/ void get_speed() { int i,j; cin>>init_speed>>row>>coloumn; speed[0][0]=init_speed; for(i=0;i<row;i++) for(j=0;j<coloumn;j++) { cin>>elevation[i][j]; speed[i][j]=init_speed*pow(2.0,elevation[0][0]-elevation[i][j]); } } void spfa( ) { queue<location>Que; location dequeue,enqueue; int i,j; /*initialization*/ for(i=0;i<row;i++) { for(j=0;j<coloumn;j++) { dis[i][j]=inf; within[i][j]=0; } } dequeue.x=0; dequeue.y=0; dis[0][0]=0; within[0][0]=1; Que.push(dequeue); while(!Que.empty()) { int xadj,yadj; dequeue=Que.front(); Que.pop(); within[dequeue.x][dequeue.y]=0; for(i=0;i<4;i++) { xadj=dequeue.x+direction[i].x; //adjacent location yadj=dequeue.y+direction[i].y; if(xadj>=0&&xadj<row&&yadj>=0&&yadj<coloumn) { double time=1.0/speed[dequeue.x][dequeue.y]; if(dis[xadj][yadj]>dis[dequeue.x][dequeue.y]+time) //relax { dis[xadj][yadj]=dis[dequeue.x][dequeue.y]+time; if(within[xadj][yadj]==0) { within[xadj][yadj]=1; enqueue.x=xadj; enqueue.y=yadj; Que.push(enqueue); //enqueue } } } } } } int main() { get_speed(); spfa(); printf("%.2f\n",dis[row-1][coloumn-1]); return 0; }
Discuss中讨论说用queue会TLE,建议用stack。我用stack后,还是TLE。然后参考 ,将邻接表用边表+firstarc数组所替代,结果还是TLE。最后将cin换成了scanf、cout换成了printf,才AC了。
源代码:
3159 | Accepted | 2128K | 672MS | C++ | 1480B | 2013-08-21 20:18:24 |
#include <iostream> #include <stack> using namespace std; #define MAXN 30001 #define MAXM 150001 #define inf INT_MAX struct EDGE { int v,weight; int next; }; EDGE edge[MAXM]; int firstarc[MAXN]; /*create a graph by using an adjacency list*/ void CreatGraph(int n, int m) { int i,position=1; int start,end,wei; memset(firstarc,0,sizeof(firstarc)); for(i=0;i<m;i++) { scanf("%d%d%d",&start,&end,&wei); edge[position].v=end; edge[position].weight=wei; edge[position].next=firstarc[start]; firstarc[start]=position++; } } void spfa(int n, int m) { stack<int>spfa_stack; int i, poping; //poping represents the poping element of the stack int *dis=new int [n]; int *within=new int [n]; /*initialization*/ fill(dis,dis+n+1,inf); memset(within,0,(n+1)*sizeof(int)); dis[1]=0; within[1]=1; spfa_stack.push(1); while(!spfa_stack.empty()) { poping=spfa_stack.top(); spfa_stack.pop(); within[poping]=0; for(i=firstarc[poping];i;i=edge[i].next) { if(dis[edge[i].v]>dis[poping]+edge[i].weight) //relax { dis[edge[i].v]=dis[poping]+edge[i].weight; if(!within[edge[i].v]) // push stack { spfa_stack.push(edge[i].v); within[edge[i].v]=1; } } } } printf("%d\n",dis[n]); delete []dis; delete []within; } int main() { int n,m; scanf("%d%d",&n,&m); CreatGraph(n,m); spfa(n,m); return 0; }