P2868 [USACO07DEC]Sightseeing Cows G 题解
我被裸题吊打。
首先这题有一个比较奇怪的性质:奶牛的最优路径一定是简单环。具体证明洛谷上有。然后就成了最优比率环问题,二分答案判断负环即可。
但是负环到底怎么判呢?跑\(n\)遍spfa?我去翻题解,发现只要把\(n\)个点同时入队,只需要跑一遍spfa就可以了。很多人写的神仙优化?
当然判正环也是可以的。
此题解最大意义在于复习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;
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];
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]=d*t[j]-a[i];//点权下放
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]<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;
}