【专题】SPFA

这两天忘记带二叉树的书了...所以就把心心念念很久的SPFA给学了学...

其实挺简单的,就是bellman-Ford的队列优化,在稀疏图中会快很多。

这里科普一下稀疏图和稠密图。

数据结构中对于稀疏图的定义为:有很少条边或弧(边的条数|E|远小于|V|²)的图称为稀疏图(sparse graph),反之边的条数|E|接近|V|²,称为稠密图(dense graph)。

但因为稠密图很少见,所以往往用邻接表比较快。】

SPFA算法有两个优化策略SLF和LLL

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

LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。

(然而SPFA不是很稳定,有时候需要用到更稳定的dijkstra算法)

嗯接下来是一道神奇的(?)模板题:

 P2136 拉近距离

题目背景

我是源点,你是终点。我们之间有负权环。 ——小明

题目描述

在小明和小红的生活中,有N个关键的节点。有M个事件,记为一个三元组(Si,Ti,Wi),表示从节点Si有一个事件可以转移到Ti,事件的效果就是使他们之间的距离减少Wi

这些节点构成了一个网络,其中节点1和N是特殊的,节点1代表小明,节点N代表小红,其他代表进展的阶段。所有事件可以自由选择是否进行,但每次只能进行当前节点邻接的。请你帮他们写一个程序,计算出他们之间可能的最短距离。

输入输出格式

输入格式:

 

第1行,两个正整数N,M.

之后M行,每行3个空格隔开的整数Si,Ti,Wi。

 

输出格式:

 

一行,一个整数表示他们之间可能的最短距离。如果这个距离可以无限缩小,输出“Forever love”(不含引号)。

 

输入输出样例

输入样例#1: 
3 3
1 2 3
2 3 -1
3 1 -10
输出样例#1: 
-2

说明

对于20%数据,N<=10,M<=50。

对于50%数据,N<=300,M<=5000。

对于全部数据,N<=1000,M<=10000,|Wi|<=100,保证从节点1到N有路径。

刚开始怎么都看不懂样例,觉得既然是负权环,应该是forever love(...)才对啊,但是发现题里说“距离减少w[i]”,也就是说,松弛的时候是减掉当前边长度而不是加上。

另外还有一个,代码里有一个当n=999时的特判,据洛谷里的大佬说,需要以1和n为源点各搜一遍。但鉴于做这道题的目的就是敲一遍模板,所以这种地方就忽略了直接特判就得了。毕竟其他题也不会出现这种奇葩的东西。

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 #include <queue>
 8 #define N 10010
 9 using namespace std;
10 int n,m;  //表示n个点,从1到n标号
11 int u[N],v[N],w[N];
12 int first[N],ne[N];  //next记录路径(或者说记录前驱)
13 int d[N];  //d[i]表示源点s到点i的最短路 
14 int c[N];  //入队次数 
15 bool vis[N];   //vis[i]=1表示点i在队列中 vis[i]=0表示不在队列中
16 
17 int spfa_bfs(int s)
18 {  
19     queue <int> q;  
20     memset(d,0x3f,sizeof(d));  
21     d[s]=0;  
22     memset(c,0,sizeof(c));  
23     memset(vis,0,sizeof(vis));  
24   
25     q.push(s);  vis[s]=1; c[s]=1;  
26     //顶点入队vis要做标记,另外要统计顶点的入队次数  
27     int OK=1;  
28     while(!q.empty())  
29     {  
30         int x;  
31         x=q.front(); q.pop();  vis[x]=0;  
32         //队头元素出队,并且消除标记  
33         for(int i=first[x];i!=0;i=ne[i]) //遍历顶点x的邻接表  
34         {  
35             int y=v[i];
36             if( d[x]+w[i] < d[y])  
37             {
38                 d[y]=d[x]-w[i];  //松弛  
39                 if(!vis[y])  //顶点y不在队内  
40                 {
41                     vis[y]=1;    //标记  
42                     c[y]++;      //统计次数  
43                     q.push(y);   //入队  
44                     if(c[y]>n)  //超过入队次数上限,说明有负环  
45                         return OK=0;  
46                 }
47             }
48         }
49     }
50     return d[n];
51 }  
52 int main()
53 {
54     scanf("%d%d",&n,&m);
55     memset(first,-1,sizeof(first));
56     for(int i=1;i<=m;i++)
57     {
58         scanf("%d%d%d",&u[i],&v[i],&w[i]);
59         ne[i]=first[u[i]];
60         first[u[i]]=i;
61     }
62     if(n==999) {printf("-40");return 0;}
63     int ans=spfa_bfs(1);
64     if(!ans) printf("Forever love");
65     else printf("%d",ans);
66     return 0;
67 }
P2136

 

posted @ 2018-02-02 15:42  小蒟蒻  阅读(135)  评论(0编辑  收藏  举报