AcWing 算法提高课 SPFA求负环

1、方法:

 

 

 一般推荐方法(2),最差时间复杂度较低

模板:

int n,m,w;
const int N=510;
vector<int> adj[N];
vector<int> cost[N];
int dis[N];
bool st[N];
int cnt[N];
bool SPFA()
{
    memset(dis,0,sizeof(dis));
    memset(st,0,sizeof(st));
    memset(cnt,0,sizeof(cnt));
    queue<int> que;
    fore(i,1,n)
    {
        st[i]=true;
        que.push(i);
    }
    while(que.size())
    {
        int u=que.front();
        que.pop();
        st[u]=false;
        
        auto &nxts=adj[u];
        auto &costs=cost[u];
        for(int i=0;i<nxts.size();i++)
        {
            int nxt=nxts[i];
            int w=costs[i];
            if(dis[nxt]>dis[u]+w)
            {
                dis[nxt]=dis[u]+w;
                cnt[nxt]=cnt[u]+1;
                if(cnt[nxt]>=n)
                    return true;
                if(!st[nxt])
                {
                    st[nxt]=true;
                    que.push(nxt);
                }
            }
        }
    }
    
    return false;
}
void YD()
{
    
    cin>>n>>m>>w;
    fore(i,1,n)
    {
        adj[i].clear();
        cost[i].clear();
    }
    int a,b,c;
    while(m--)
    {
        cin>>a>>b>>c;
        adj[a].push_back(b);
        cost[a].push_back(c);
        adj[b].push_back(a);
        cost[b].push_back(c);
    }
    while(w--)
    {
        cin>>a>>b>>c;
        adj[a].push_back(b);
        cost[a].push_back(-c);
    }
    if(SPFA()) cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
}
 
View Code

注意,在初始化时将全部dis初始化为0(可以证明,不初始化也不影响,因为若存在负环则会不断循环减小路径长度);

相当于虚拟源点向全部点都有一条权重为0的边,故初始还需要将全部点加入队列中。

cnt数组用来统计每个点的最短路中所包含的边数。

 

2、求是否有正环

按1、中方法,改变不等号求正环即可(SPFA求最长路)

 

一般判断负环的时间复杂度是O(nm)可能会超时

trick:当SPFA效率较低时,可以假设存在负环,

即SPFA算法所有点入队了2n-3n次就视为存在负环。

例题:

https://www.acwing.com/problem/content/description/1167/

代码:

#include<bits/stdc++.h>

#define fore(x,y,z) for(LL x=(y);x<=(z);x++)
#define forn(x,y,z) for(LL x=(y);x<(z);x++)
#define rofe(x,y,z) for(LL x=(y);x>=(z);x--)
#define rofn(x,y,z) for(LL x=(y);x>(z);x--)
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second

using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
vector<int> adj[720];
vector<int> cost[720];
const int N=720;
bool st[720];
int cnt[720];
double dis[720];
bool Check(double mid)
{
    memset(st,0,sizeof(st));
    memset(cnt,0,sizeof(cnt));
    int tmp=0;
    queue<int> que;
    fore(i,0,N-1) que.push(i),st[i]=true;
    while(que.size())
    {
        int u=que.front();
        que.pop();
        st[u]=false;
        
        for(int i=0;i<adj[u].size();i++)
        {
            int nxt=adj[u][i];
            int c=cost[u][i];
            
            if(dis[nxt]<dis[u]+c-mid)
            {
                dis[nxt]=dis[u]+c-mid;
                cnt[nxt]=cnt[u]+1;
                tmp++;
                if(tmp>5000) return true;
                if(cnt[nxt]>=N) return true;
                if(!st[nxt])
                {
                    st[nxt]=true;
                    que.push(nxt);
                }
            }
        }
    }
    return false;
}
int n;
void YD()
{

    string str;
    fore(i,0,N)
    {
        adj[i].clear();
        cost[i].clear();
    }
    while (n -- ){
        cin>>str;
        if(str.size()>=2)
        {
            int num1=(str[0]-'a')*26+str[1]-'a';
            int num2=(str[str.size()-2]-'a')*26+str[str.size()-1]-'a';
            adj[num1].push_back(num2);
            cost[num1].push_back(str.size());
        }
    }

    double l=0,r=1000;
    while(r-l>1e-4)
    {
        double mid=(r+l)/2;
        if(Check(mid))
        {
            l=mid;
        }
        else
        {
            r=mid;
        }
    }
    if(l-0<1e-3)
    {
        cout<<"No solution"<<endl;
        
    }
    else
        cout<<l<<endl;
}
 
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    
    while (cin>>n,n)
    {
        YD();
    }
    return 0;
}
View Code

tmp统计修改距离次数

#include<bits/stdc++.h>

#define fore(x,y,z) for(LL x=(y);x<=(z);x++)
#define forn(x,y,z) for(LL x=(y);x<(z);x++)
#define rofe(x,y,z) for(LL x=(y);x>=(z);x--)
#define rofn(x,y,z) for(LL x=(y);x>(z);x--)
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second

using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
vector<int> adj[720];
vector<int> cost[720];
const int N=720;
bool st[720];
int cnt[720];
double dis[720];
bool Check(double mid)
{
    memset(st,0,sizeof(st));
    memset(cnt,0,sizeof(cnt));
    int tmp=0;
    queue<int> que;
    fore(i,0,N-1) que.push(i),st[i]=true;
    while(que.size())
    {
        int u=que.front();
        que.pop();
        st[u]=false;
        
        for(int i=0;i<adj[u].size();i++)
        {
            int nxt=adj[u][i];
            int c=cost[u][i];
            
            if(dis[nxt]<dis[u]+c-mid)
            {
                dis[nxt]=dis[u]+c-mid;
                cnt[nxt]=cnt[u]+1;

                if(cnt[nxt]>=N) return true;
                if(!st[nxt])
                {
                    tmp++;
                    if(tmp>3000) return true;
                    st[nxt]=true;
                    que.push(nxt);
                }
            }
        }
    }
    return false;
}
int n;
void YD()
{

    string str;
    fore(i,0,N)
    {
        adj[i].clear();
        cost[i].clear();
    }
    while (n -- ){
        cin>>str;
        if(str.size()>=2)
        {
            int num1=(str[0]-'a')*26+str[1]-'a';
            int num2=(str[str.size()-2]-'a')*26+str[str.size()-1]-'a';
            adj[num1].push_back(num2);
            cost[num1].push_back(str.size());
        }
    }

    double l=0,r=1000;
    while(r-l>1e-4)
    {
        double mid=(r+l)/2;
        if(Check(mid))
        {
            l=mid;
        }
        else
        {
            r=mid;
        }
    }
    if(l-0<1e-3)
    {
        cout<<"No solution"<<endl;
        
    }
    else
        cout<<l<<endl;
}
 
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    
    while (cin>>n,n)
    {
        YD();
    }
    return 0;
}
View Code

注意在Check函数中,tmp统计总共的入队次数,如果过大则直接判断存在负环(正环)

trick2:SPFA的队列改成栈,然后用1、中方法判断

posted @ 2022-09-14 15:31  80k  阅读(24)  评论(0编辑  收藏  举报