/*
适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止
期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。
实现方法:
建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。
判断有无负环:
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
*/
#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
#define Max 0x3f3f3f3f
using namespace std;
const int maxn = 107;
//货币种类是点,兑换商店是边.亮点之间有多条边
//一共有200条边,100个点
typedef struct node
{
double c;
double r;
}node;
vector<node> map[maxn][maxn];//map[i][j].size()==0表示两点之间并没有边
bool vis[maxn];
int cnt[maxn];
double D[maxn];
vector<int> P[maxn];
bool spfa(int id,int n,double v)//只要出现收入增加的环,而且可以转化为id种货币(只要是从id中货币出去的就行)
{
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
queue<int> Q;
for (int i = 0; i < n; i++)
{
D[i] = 0;
}
Q.push(id);
vis[id] = 1;//vis数组标记队列里面的元素
cnt[id]++;//
D[id] = v;
while (!Q.empty())
{
//取出队列头部元素
int u = Q.front();
Q.pop();
vis[u] = 0;
for (int i = 0; i < P[u].size(); i++)
{
int v = P[u][i];//终点
double t = 0;//现有货币
for (int j = 0; j < map[u][v].size(); j++)
{
double tt = D[u] - map[u][v][j].c;
if ( tt >= 0)
{
t = max(t, tt*map[u][v][j].r);
}
}
if (t > D[v])//实现增长
{
D[v] = t;
if (!vis[v])//之前并没有在队列中,将其入队
{
Q.push(v);
vis[v] = 1;
cnt[v]++;
if (cnt[v] > n)//入队超过n次,说明形成无限增长
{
return true;
}
}
}
}
}
return false;
}
int main()
{
int n, m, s;
double v;
cin >> n >> m >> s >> v;
int a, b;
s--;
node an, bn;
for (int i = 0; i < m; i++)
{
cin >> a >> b >> an.r >> an.c >> bn.r >> bn.c;
a--;
b--;
map[a][b].push_back(an);
map[b][a].push_back(bn);
}
for (int i = 0; i < n; i++)//通过创建P[i]表示从i出去可以到达的边,来实现降低复杂度
{
for (int j = 0; j <= i; j++)
{
if (map[i][j].size() > 0)
{
P[i].push_back(j);
if (i != j)
{
P[j].push_back(i);
}
}
}
}
if (spfa(s, n, v))
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
return 0;
}
POJ1860_通过SPFA寻找无限正循环
最新推荐文章于 2022-03-03 09:25:10 发布