bzoj1690:[Usaco2007 Dec]奶牛的旅行(分数规划+spfa判负环)

  PS:此题数组名皆引用:戳我

       题目大意:有n个点m条有向边的图,边上有花费,点上有收益,点可以多次经过,但是收益不叠加,边也可以多次经过,但是费用叠加。求一个环使得收益和/花费和最大,输出这个比值。

       显然这就是经典的分数规划题啊,就是最优比率环,那么就二分答案,将所有边(u,v)的边权改为【v的点权-(u,v)原边权*mid】(因为d[i]=a[i]-L*b[i]),然后判一下是否有正环,有的话就说明有更优的答案(F(L)=sigma(a[i]*x[i])-L*sigma(b[i]*x[i])>0即sigma(a[i]*x[i])/sigma(b[i]*x[i])>L),缩小范围继续二分。判正环有够别扭的,那就全部改成相反数然后判负环吧233333

代码如下:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
using namespace std;
struct zs{int too,pre;double dis;}e[10001];
struct poi{int pos;double dis;};
priority_queue<poi>q;
bool operator <(poi a,poi b){return a.dis-b.dis>1e-3;}
int n,m,x,y,now,tot,num[1001],last[1001];
bool v[1001];
double l,r,mid,dis[1001],fun[1001];
bool spfa(int x)
{
    for(int i=1;i<=n;i++)dis[i]=23333333;v[x]=true;q.push((poi){1,0});dis[1]=0;
    while(!q.empty())
    {
        int i,too;
        for(i=last[now=q.top().pos],too=e[i].too,q.pop();i;i=e[i].pre,too=e[i].too)
        {
            double dist=e[i].dis*mid-fun[too];
            if(dis[too]-dis[now]-dist>1e-3)
            {
                dis[too]=dis[now]+dist;
                if(!v[too])v[too]=1,q.push((poi){too,e[i].dis}),num[too]++;
                if(num[too]>233)return 1;
            }
        }
        v[now]=0;
    }
    return 0;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lf",&fun[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d %lf",&x,&y,&e[++tot].dis);
        e[tot].too=y;e[tot].pre=last[x];last[x]=tot;
    }
    l=0;r=10000;
    while(r-l>1e-3)
    {
        memset(v,0,sizeof(v));
        memset(num,0,sizeof(num));
        mid=(l+r)/2;
        if(spfa(1))l=mid;
        else r=mid;
    }
    printf("%.2lf",l);
}
View Code
原文地址:https://www.cnblogs.com/Sakits/p/5469300.html