直接上代码吧 ,spfa判断负权环的原理这里不再阐述了
//poj3259,本题采用队列优化的SPEA算法进行写;作为算法的复习
//不得不说,这道题,一开始没读懂,后来发现,有环就好,最后最短路径是负数就好
//不过这道题更合适的是用朴素算法求解,倒也真的用不上SPFA,
//这道题仔细审读的一遍,其实,判断负环的问题,所以还是需要用贝尔曼和SPPFA问题来解;
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
int h[505],to[5505],w[5505],ne[5505]; //
long long vis[505],dis[505]; //用来记录已经有最小的点和到j点的最短距离
int cnt[505];
int t1,n,m,w1,s,e,t;
using namespace std;
int ans; //记录边的编号;
void add(int a,int b,int c){ //利用头插法将边插进去
w[ans]=c; //记录此边的权值
ne[ans]=h[a]; //记录边的下一条边
to[ans]=b; //记录此边的另一顶点
h[a]=ans++; //记录边的顶点为h[a];
}
int spfa(){
queue<int> q;
memset(vis,0,sizeof(vis));
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(cnt,0,sizeof(cnt));
q.push(1);
dis[1]=0; //这里直接从1开始;
vis[1]=1;
cnt[1]++;
while(!q.empty()){
int now=q.front();
q.pop();
vis[now]=0;
for(int i=h[now];i!=-1;i=ne[i]){
int j=to[i];
if(dis[j]>dis[now]+w[i]){ //进行松弛操作
dis[j]=dis[now]+w[i];
if(!vis[j]){ //是否将j放入队列中
q.push(j);
vis[j]=1;
cnt[j]++;
if(cnt[j]>=n) return 1;
}
}
}
}
return 0;
}
int main()
{
int a,b,c; //用来记录边的
cin>>t1;
while(t1--){
ans=1;
memset(h,-1,sizeof(h));
cin>>n>>m>>w1;
for(int i=1;i<=m;i++)
{
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=w1;i++)
{
cin>>s>>e>>t;
add(s,e,-t);
}
if(spfa()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
不过这道题 我RE了好几次,记得每次初始化都要清空,不然真的会RE,哦 也对 这也是正确的做法哈哈哈;接下来也会出几种最短路径的区别总结
上面的代码有个松弛计算的地方写进了if里面,其实,这里直接写在松弛操作的下面更好理解,下面为修改版本
//poj3259,本题采用队列优化的SPEA算法进行写;作为算法的复习
//不得不说,这道题,一开始没读懂,后来发现,有环就好,最后最短路径是负数就好
//不过这道题更合适的是用朴素算法求解,倒也真的用不上SPFA,
//这道题仔细审读的一遍,其实,判断负环的问题,所以还是需要用贝尔曼和SPPFA问题来解;
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
int h[505],to[5505],w[5505],ne[5505]; //
long long vis[505],dis[505]; //用来记录已经有最小的点和到j点的最短距离
int cnt[505];
int t1,n,m,w1,s,e,t;
using namespace std;
int ans; //记录边的编号;
void add(int a,int b,int c){ //利用头插法将边插进去
w[ans]=c; //记录此边的权值
ne[ans]=h[a]; //记录边的下一条边
to[ans]=b; //记录此边的另一顶点
h[a]=ans++; //记录边的顶点为h[a];
}
int spfa(){
queue<int> q;
memset(vis,0,sizeof(vis));
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(cnt,0,sizeof(cnt));
q.push(1);
dis[1]=0; //这里直接从1开始;
vis[1]=1;
cnt[1]++;
while(!q.empty()){
int now=q.front();
q.pop();
vis[now]=0;
for(int i=h[now];i!=-1;i=ne[i]){
int j=to[i];
if(dis[j]>dis[now]+w[i]){ //进行松弛操作
cnt[j]++; //松弛一次就计数一次
dis[j]=dis[now]+w[i];
if(!vis[j]){ //是否将j放入队列中
q.push(j);
vis[j]=1;
}
if(cnt[j]>=n) return 1;
}
}
}
return 0;
}
int main()
{
int a,b,c; //用来记录边的
cin>>t1;
while(t1--){
ans=1;
memset(h,-1,sizeof(h));
cin>>n>>m>>w1;
for(int i=1;i<=m;i++)
{
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=w1;i++)
{
cin>>s>>e>>t;
add(s,e,-t);
}
if(spfa()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
两个代码都能AC,因为都会陷入死循环哈哈,不过下面这个代码个人觉得思路跟对,如有问题,可ddQQ:3379864552