1.算法描述
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,若不为空,重复操作。
用邻接表(adjacency list)作为图的存储结构,SPFA算法时间复杂度为O(ke)。
2. 图的邻接表表示
在邻接表中,对每个顶点u建立一个单链表。单链表由c(u)个表结点组成,c(u)表示节点u的出度;每一个单链表都有一个表头结点。
2.1 表结点
| adjvex | weight | next |
--------------------------------
2.2 头结点
| vertex | firstarc |
------------------------
3. Referrence
4.问题
4.1 POJ 1511
#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;
}
4.2 POJ 3037
题目大意:有一个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;
}
4.3 POJ 3159
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;
}