做前分析
道路距离最短+有负权边==spfa。
起点s,到各个点距离计算,spfa一遍即可,最后循环一遍dis数组就行了
但是,老话说:’spfa好像活着,其实已经死了 ‘,几乎所有一眼spfa题目全部需要优化才行。当初学spfa时,老师口口声声说,时间复杂度最大O(nm),大概率O(m);结果全是O(nm),哭哭哭哭。所以以后遇到spfa算法直接写优化的
SPFA的两种优化
SLF优化
对即将入队的元素进行操作,与队头元素的dis值进行比较,若大于或此时队列为空,放入队尾;若小于,放入队头。(原理不太清楚,查半天没有,大概就是先遍历小的部分更容易吧,有兄弟能给我解释解释吗?)
//对队列头尾都操作,要用双端队列deque
queue<int>s;
s.push_back();//在队尾插入
s.push_front();//在队头插入
s.pop()_back();//删除队尾元素
s.pop_front();//删除队头元素
//其余操作与queue相同,如s.empty(),s.size();
//SLF优化部分代码
//入队是在dis[j]被更新时
//t为此时已经确定最短距离的点,用他来更新与之相连的点到起点的距离
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];//枚举与t相连的点j
if(dis[j]>dis[t]+w[i])//dis[j]要被更新
{
dis[j]=dis[t]+w[i];
if(!st[j])//如果此时j不在队列中
{
st[j]=1;
//SLF优化
if(!q.empty()&&dis[j]<dis[q.front()])q.push_front(j);
else q.push_back(j);
}
}
}
LLL优化
对出队元素进行操作,队列中所有dis数组值 和为sum,个数为num;如果出队元素t的dis[t]*num>sum,则把t从队头换到队尾(就是dis[t]大于现在在队列中dis的平均值)。
deque<int>q;
dis[s]=0;
q.push_back(s);//s是起点
int sum=0,num=1;//dis[s]为0,所有sum初始为0,队列只有s一个元素,num=1'
while(q.size())
{
//队头元素出队操作
int t=q.front();
while(!q.size())
{
if(dis[t]*num>sum)//dis[t]大于现在队列的DIS平均值
{
把t从队头调换到队尾
q.pop_front();
q.push_back(t);
t=q.front();//一样的操作直到找到小于平均值的元素
}
}
q.pop_front(); st[t]=0;
num--; sum-=dis[t];
加入两种优化的spfa代码
注意点:本题道路为无向图,而航路为有向,则N=2*R+P=150010;
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=150010;
typedef pair<int,int> pll;
#define x first
#define y second
int h[N],e[N],w[N],ne[N],idx;
int t,r,p,s;
int dis[N],st[N];
void add(int a,int b,int c)
{
e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++;
}
void spfa()
{
memset(dis,0x3f,sizeof dis);
dis[s]=0; st[s]=1;
deque<int>q;
q.push_back(s);
int sum=0,num=1;
while(q.size())
{
int t=q.front();
while(!q.size())
{
if(dis[t]*num>sum)
{
q.pop_front();
q.push_back(t);
t=q.front();
}
}
q.pop_front(); st[t]=0;
num--;
sum-=dis[t];
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dis[j]>dis[t]+w[i])
{
dis[j]=dis[t]+w[i];
if(!st[j])
{
st[j]=1;
sum+=dis[j]; num++;
if(!q.empty()&&dis[j]<dis[q.front()])q.push_front(j);
else q.push_back(j);
}
}
}
}
}
int main()
{
memset(h,-1,sizeof h);
cin>>t>>r>>p>>s;
for(int i=1;i<=r;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c); add(b,a,c);
}
for(int i=1;i<=p;i++)
{
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
spfa();
for(int i=1;i<=t;i++)
{
if(dis[i]==0x3f3f3f3f)printf("NO PATH\n");
else cout<<dis[i]<<endl;
}
return 0;
}