一、spfa
1、算法
这东西属于bellmen-ford的优化,恕我直言,没看出来,到有点像dijkstra的改版,改成可以判负环的形式,时间复杂度是O(m)->O(n*m)的,看出题人想不想卡了(不过一般情况,只要出题人没问题,他都要卡一下)
开始演示:
设:dist[i]为源点S到i点的路经长度。
由松弛操作我们可知:dist[v] = min(dist[v], dist[u] + w);只有当dist[u]变小的时候,dist[v]才会变小,所以我们可以用变小的点,去更新该点所连通的点。
然后就不演示了,结束!
一句话思想
用已经变小的点去更新可以变小的点
2、如何判负环呢?
记录每一条路径所走的边数,如果边数一共N个点,一条路最多有n-1条边。所以说如果又一条路径的边树>= N,那么就一定有负环。
但是注意,这个负环是从起点走到终点时会出现的负环。有可能一张图上有负环,但你走到终点都遇不到。
二、代码演示
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int N=1e5+7;
struct one_tu
{
int w,next,to;
}tu[N];
int top = 0,head[N];
void build(int u,int v,int w)
{
top++;
tu[top] = one_tu{w,head[u],v};
head[u] = top;
}
int n,m;
int dist[N],cnt[N];//记录路经长度(赋值最大值)、边数(初始值为0)
bool jud[N];//判断是否走过
bool spfa(int s)
{
memset(dist,0x3f,sizeof(dist));
dist[s] = 0;
queue<int> q;
q.push(s);//添加第一个点
jud[s] = true;//第一个点入对
while(!q.empty())
{
int u = q.front();
q.pop();
jud[u] = false;//该点出队,方便进行接下来的更新
for(int i = head[u]; i; i = tu[i].next)
{
int v = tu[i].to, w = tu[i].w;
if(dist[v] > dist[u] + w)//松弛操作
{
dist[v] = dist[u] + w;
cnt[v] = cnt[u] + 1;//记录走了多少条边
if(cnt[v] >= n)return true;//存在负环
if(!jud[v])
{
q.push(v);
jud[v] = true;//将减少权值的点入对
}
}
}
}
return false;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0; i < m; i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
build(u,v,w);
}
int s,t;scanf("%d%d",&s,&t);
if(spfa(s))printf("No\n");
else printf("%d\n",dist[n]);
return 0;
}