SPFA的两种优化(SLF+LLL)
d[i]:i到达起始节点的目前最小值。
q:deque,双端队列。
sum:队列中元素和。
SLF:Small Label First 策略,设要加入的节点是j,若d[j]<d[q.front()],则将j插入队首,否则插入队尾
LLL:Large Label Last 策略,设队首元素为i,队列中所有d值的平均值为x,若d[i] * (int)q.size() <= sum,则将i插入到队首(注意,d[i]的值可能相比之前计算到sum时已经发生改变,更新了,但是更新只会使得d[i]变小(因为只有比之前小才会发生改变),我们知道插入队首的条件为d[i] * (int)q.size() <= sum,当d[i]小了,如果原来使得插入队首的条件满足,那么现在比之前小也能满足条件),否则,则插入队尾并且查找下一元素,直到找到某一i使得d[i] * (int)q.size() > sum,则将i出队进行松弛操作。(为什么q.size()要强制转换成int类型,这是因为在C标准中当int类型*unsigned类型时,结果为unsigned类型,且d[i]会先转换成unsigned类型与q.size()进行相乘,这样-1转换成unsigned类型的值就为unsigned类型最大值,不是-1,所以可能造成程序无限循环)。
代码:
const int INF = 0x3fffffff; struct Node{ int to; int value; Node (int x, int y){ to = x; value = y; } }; vector<Node> v[505];//邻接表对于v[i]连结的节点和权值(to是节点,value权值) int d[505];//d[i]表示i到起始节点的距离 bool is_que[505];//是否在队列中 int cnt[505];//进队列的次数 bool spfa(int i, int n)//i起始节点,n节点数 { memset(cnt, 0, sizeof(cnt)); memset(is_que, 0, sizeof(is_que)); fill(d, d + 505, INF); d[i] = 0; cnt[i] = 1; deque<int> q; is_que[i] = true; q.push_back(i); int sum = d[i]; while(!q.empty()) { int t = q.front(); while(d[t] * (int)q.size() > sum)//SLF策略 { int p = q.size(); q.push_back(t); q.pop_front(); t = q.front(); } q.pop_front(); sum -= d[t];//记得出了队列剪除原来的值 is_que[t] = false; for(int j = 0; j < v[t].size(); j++) { if(v[t][j].value + d[t] < d[v[t][j].to]) { d[v[t][j].to] = v[t][j].value + d[t]; if(!is_que[v[t][j].to]) { is_que[v[t][j].to] = true; if(!q.empty() && d[v[t][j].to] >= d[q.front()])//LLL策略 q.push_back(v[t][j].to); else q.push_front(v[t][j].to); sum += d[v[t][j].to]; if(++cnt[v[t][j].to] > n) return false; } } } } return true; }