题意:
给定n个点,每个点有一个开心度F[i],每个点有m条单向边,每条边有一个长度d,要求一个环,使得它的 开心度的和/长度和 这个比值最大。
n<=1000,m<=5000
题解:
最优比率环,很像以前做过的一题最优比率生成树。
首先二分一个答案r=sigma(xi*fi)/sigma(xi*di),设z=r*sigma(xi*di)-sigma(xi*fi),若能找到一个环满足z<=0,则代表sigma(xi*fi)>=r*sigma(xi*di),则r可以比当前的r要大,则l=mid;否则r=mid;
判断能否有一个环满足让z<=0,我们就让所有的边权=r*di-f[a[i].x],然后用spfa判负环即可。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std; const int N=,M=;
const double INF=(double)1e9;
int n,m,len,first[N],cnt[N];
double dis[N],F[N];
bool in[N];
struct node{
int x,y,next;
double d,w;
}a[*M];
queue<int> q; double mid; double myabs(double x){return x> ? x:-x;} void ins(int x,int y,double d)
{
a[++len].x=x;a[len].y=y;a[len].d=d;
a[len].next=first[x];first[x]=len;
} double spfa(int st)
{
while(!q.empty()) q.pop();
for(int i=;i<=n;i++) dis[i]=INF;
memset(cnt,,sizeof(cnt));
memset(in,,sizeof(in));
q.push(st);dis[st]=;in[st]=;
while(!q.empty())
{
int x=q.front();in[x]=;cnt[x]++;q.pop();
if(cnt[x]==n) return ;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(dis[y]>=dis[x]+a[i].w)
{
dis[y]=dis[x]+a[i].w;
if(!in[y]) in[y]=,q.push(y);
}
}
}
return ;
} bool check(double r)
{
for(int i=;i<=len;i++) a[i].w=r*a[i].d-F[a[i].x];
return spfa();
} int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
len=;
memset(first,,sizeof(first));
double l=,r=;
for(int i=;i<=n;i++)
{
scanf("%lf",&F[i]);
r+=F[i];
}
for(int i=;i<=m;i++)
{
int x,y;double d;
scanf("%d%d%lf",&x,&y,&d);
ins(x,y,d);
}
while(l<r)
{
mid=(l+r)/;
if(check(mid)) l=mid;
else r=mid;
if(myabs(l-r)<=0.0000001) break;
}
mid=6.0;
if(myabs(l)<=0.0000001) printf("0\n");
else printf("%.2lf\n",l);
return ;
}