【POJ3259】SPFA判断负权环

直接上代码吧 ,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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值