P2868 [USACO07DEC]Sightseeing Cows G 題解
阿新 • • 發佈:2020-08-08
事後來看,這題就是道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; }
當然判正環也是可以的,程式碼長得太像就不放了。