1. 程式人生 > 其它 >spfa判負環(01分數規劃)

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);
}