spfa判负环(01分数规划)

spfa 判断负环的话

1.某个点出队n次,存在负环。

2.某条路径上的边数大于等于n,说明存在负环。(通常采用这种)

 

0 1 分数划分的话(通常配合二分做)

比如 题目求  ∑wf[i] /  ∑wt[i]  > mid     ==>   ∑( wf [i]  - mid * wt[i] ) > 0   ,那这样就是在求是否存在 正环。

而通常的spfa时判断存不存在负环 那怎么变呢,  第一种 可以把上面 不等式填个负号, 转化成求 负环,第二种 就是 我转变一下思路,假如存在负环,假如 当前这个点是t,它的邻接点是就 j ,dist[j]的更新条件是dist[t]+(wf [i]  - mid * wt[i])< dist[ j ],那么同样的假如存在正环,我们只要改变一下更新判断的条件,  dist[t]+(wf [i]  - mid * wt[i])>dist[ j ]  ,我们就更新一下 dist[ j ] 。

 

这里其实有个trick,就是当数据很多的时候,看更新的点数已经是原来的几十倍的话,就有大概率是环,经验值。

 

题目的话就是,

AcWing 361

观光奶牛

//0 1分数规划
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=1010,M=5010;

int n,m;
int wf[N];
int h[N],e[M],ne[M],wt[M],idx;
int q[N],cnt[N];
double dist[N];
bool st[N];

bool check(double mid)
{
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    
    int hh=0,tt=0;
    for(int i=1;i<=n;++i)
    {
        q[tt++]=i;
        st[i]=true;
    }
    
    while(hh!=tt)
    {
        int t=q[hh++];
        if(hh==N) hh=0;
        st[t]=false;
        
        for(int i=h[t];i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]<dist[t]+wf[t]-mid*wt[i])
            {
                dist[j]=dist[t]+wf[t]-mid*wt[i];
                cnt[j]=cnt[t]+1;
                if(cnt[j]>=n) return true;
                if(!st[j])
                {
                    q[tt++]=j;
                    if(tt==N) tt=0;
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
void add(int a,int b ,int c)
{
    e[++idx]=b,ne[idx]=h[a],h[a]=idx,wt[idx]=c;
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i) scanf("%d",&wf[i]);
    
    while(m -- )
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    double l=0,r=1000;
    while(r-l>1e-4)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    
    printf("%.2lf",l);
    return(0);
}

 

AcWing 1165

单词环

(把一个点拆成 一条边 点数就会下降到26*26=676个,然后还要加小trick才能过)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=700,M=1e5+10;

int n,m;
int q[N],cnt[N];
double dist[N];
int h[N],e[M],ne[M],w[M],idx;
bool st[N];

void add(int a,int b,int c)
{
    e[++idx]=b,ne[idx]=h[a],h[a]=idx,w[idx]=c;
}

bool check(double mid)
{
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    
    int hh=0,tt=0;
    
    for(int i=0;i<676;++i) //26*26=676
    {
        q[tt++]=i;
        st[i]=true;
    }
    
    int count=0;
    while(hh!=tt)
    {
        int t=q[hh++];
        if(hh==N) hh=0;
        st[t]=false;
        
        for(int i=h[t];i;i=ne[i])
        {
            int j=e[i];
            if(dist[j] < dist[t]+w[i]-mid)
            {
                dist[j]=dist[t]+w[i]-mid;
                cnt[j]=cnt[t]+1;
                if(++ count > 10000) return true; //trick 经验值
                if(cnt[j]>= N) return true;
                if(!st[j])
                {
                    q[tt++]=j;
                    if(tt==N) tt=0;
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
int main()
{
    char str[1010];
    while(scanf("%d",&n),n)
    {
        idx=0;
        memset(h,0,sizeof h);
        for(int i=0;i<n;++i)
        {
            scanf("%s",str);
            int len=strlen(str);
            int a=(str[0] - a) * 26 + str[1] - a;
            int b=(str[len-2] - a) * 26 + str[len-1] - a;
            add(a,b,len);
        }
        if(!check(0)) puts("No solution");
        else{
            double l=0,r=1001;
            while(r-l>1e-4)
            {
                double mid=(l+r)/2;
                if(check(mid)) l=mid;
                else r=mid;
            }
            printf("%.2lf\n",l);
        }
    }
    return(0);
}

 

spfa判负环(01分数规划)

上一篇:最小栈


下一篇:AtCoder Beginner Contest 155 题解