1. 程式人生 > 實用技巧 >P2868 [USACO07DEC]Sightseeing Cows G 題解

P2868 [USACO07DEC]Sightseeing Cows G 題解

事後來看,這題就是道01分數規劃裸題,但是我還是做了很久。。可能我還是太弱了。

首先這題有一個比較奇怪的性質:奶牛走過的最優路徑一定是一個簡單環。具體證明就不說了,可以去洛谷找找。然後我們發現這是一個最優比率環問題,二分答案判斷負環即可。

但是負環到底怎麼判呢?以前寫的負環模板是從給定起點出發的,顯然不能用在這個題上面(跑\(n\)遍spfa會TLE)。然後我去翻題解,發現有很多用基於棧或者dfs的spfa水過去的。其實這些都不是正解,我們只要把\(n\)個點同時入隊,只需要跑一遍spfa就可以了。

程式碼:

#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<ctime>
#include<iostream>
#include<vector>
#include<algorithm>
#include<utility>
#include<queue>
using namespace std;
typedef pair<int,int> pii;
#define forg(i,x) for(int i=first[x];i;i=nxt[i])
#define uu unsigned
#define fi first
#define se second
#define ran() ((unsigned)rand())
#define lam(z,k) [&](const z &a,const z &b){ return k; }
#define od(x) ((x)&1)
#define ev(x) (od(x)^1)
#define scanf a1234=scanf
int a1234;
char buf[1<<18],*bufs=buf,*buft=buf;
inline int gc(){
    return bufs==buft&&(buft=(bufs=buf)+fread(buf,1,1<<18,stdin)),bufs==buft?-1:*bufs++;
}
inline void xxx(){for(;;);}

int n,m;const int mxn=1003,mxm=5003;
int a[mxn],t[mxm];
double w[mxm];
int to[mxm],nxt[mxm],first[mxn],tot=0;
inline void gadd(int x,int y,int tt){
    to[++tot]=y,nxt[tot]=first[x],first[x]=tot,t[tot]=tt;
}
bool vis[mxn],cir[mxn];
inline bool hdfs(int x){
    vis[x]=1;
    forg(i,x)if(vis[to[i]])return 1;else if(hdfs(to[i]))return 1;
    return 0;
}

bool inq[mxn];int cnt[mxn];
double dis[mxn];
queue<int>q;
inline bool spfa(double d){
    for(int i=1;i<=n;++i)forg(j,i)w[j]=a[i]-d*t[j];
    q=queue<int>();
    for(int i=1;i<=n;++i)q.push(i),dis[i]=0,cnt[i]=0,inq[i]=1;
    while(q.size()){
        int x=q.front();q.pop();
        inq[x]=0;
        forg(i,x)if(dis[x]+w[i]+1e-8>dis[to[i]]){
            dis[to[i]]=dis[x]+w[i],cnt[to[i]]=cnt[x]+1;
            if(cnt[to[i]]>=n)return 1;
            if(!inq[to[i]])q.push(to[i]),inq[to[i]]=1;
        }
    }
    return 0;
}

int main(){
    scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%d",a+i);for(int i=1,u,v,tt;i<=m;++i){scanf("%d%d%d",&u,&v,&tt);if(u!=v)gadd(u,v,tt);}
    bool tg=0;
    for(int i=1;i<=n;++i){memset(vis,0,sizeof(vis));if(hdfs(i))tg=1;}
    if(!tg)return puts("0"),0;
    double l=1e-3,r=1e3,mid;
    while(r-l>1e-4){
        mid=(l+r)/2;
        if(spfa(mid))l=mid;else r=mid;
    }
    printf("%.2f\n",l);
    return 0;
}


當然判正環也是可以的,程式碼長得太像就不放了。