BZOJ 1690 【USACO 2007 Dec奶牛观光】

Description
作为对奶牛辛勤工作的回报,约翰决定带她们去附近的大城市玩一天。这个城市有L个景点,参观第i个景点会给奶牛带来Fi点欢乐度。第二天一早,奶牛可以自由选择从一个景点出发,约翰会负责开车把她们送到那里,但她们晚上必须回到这个景点和约翰汇合。

大城市里都是单行道,第i条道路从第L1i个建筑通向第L2i个建筑,走完需要Ti的时间。奶牛讨厌走路,定义一条游览线路的“欢乐指数”为参观这条线路上所有景点的欢乐度之和与花在路上的时间之和的比值。欢乐指数越大的线路越受欢迎。当然,参观同一景点两次不会带来双倍的欢乐。假设奶牛们至少参观两个景点,请帮她们找到一条欢乐指数最大的线路。

Input Format
第一行:两个用空格分开的整数:L和P,1 ≤ L ≤ 1000,2 ≤ P ≤ 5000

第二行到L + 1行:第i + 1行有一个整数Fi,1 ≤ Fi ≤ 1000

第L + 2行到L + P + 1行:第i + L + 1行有三个用空格分开的整数:L1i,L2j和Ti,1 ≤ L1i ≤ L,1 ≤ L2i ≤ L,1 ≤ Ti ≤ 1000

Output Format
第一行:输出一个实数,表示最大的欢乐指数,保留两位小数

Sample Input
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
Sample Output
6.00
Hint
(1 → 2 → 3 → 5 → 1,总乐趣值为60,花费在路上的时间为10)

【题目大意】求一个环的{点权和}除以{边权和},使得那个环在所有环中{点权和}除以{边权和}最大。
【题解】
最优比例环(比uva11090多了个点权)
∑(i=1,n)v[i]/∑(i=1,n)e[i]最大的环(n为环的点数),
即对于所有的环都有 ∑(i=1,n)(v[i])/∑(i=1,n)(e[i])<=ans
∑(i=1,n)(ans*e[i]-v[i]) >=0
二分答案 若mid < ans时,就存在环使得∑(i=1,n)(mid*e[i]-v[i])<0,即有负环(重构边权为mid*e[i]-v[i]); 若mid >=ans时,就对于所有的环∑(i=1,n)(mid*e[i]-v[i])>=0,即没有负权回路。 spfa验证负环


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
double a[1500],d[1500],l,r,mid,o;
struct info
  {
    int ar,next;
    double l;
  }road[10005];
int first[1500],i,j,k,m,num,n,q[1500],c[1500],f[1500],p1,q1;
void add(int x,int y,double len)
  {
     num++;
     road[num].ar=y;road[num].next=first[x];
     road[num].l=len;first[x]=num;
  }
int spfa(double k)
  {
    int i,h,t,u,v;
    memset(c,0,sizeof(c));
    memset(f,0,sizeof(f));
    for (i=1;i<=n;i++) d[i]=1e9;
    t=1;h=0;q[1]=1;d[1]=0;f[1]=1;c[1]=1;
    for (;h!=t;)
      {
        h++;if (h==1500) h=0;u=q[h];f[u]=0;
        for (i=first[u];i;i=road[i].next)
          {
            v=road[i].ar;
            if (d[u]+k*road[i].l-a[v]<d[v]) 
              {
                d[v]=d[u]+k*road[i].l-a[v];
                if (!f[v])
                  {
                    t++;if (t==1500) t=0;
                    q[t]=v;f[v]=1;c[v]++;
                    if (c[v]>n) return 1;
                  }
              }
          } 
      }
    return 0;
  }
int main()
  {
    scanf("%d%d",&n,&m);r=0;l=0;
    for (i=1;i<=n;i++) scanf("%lf",&a[i]),r+=a[i];
    for (i=1;i<=m;i++)
      {
        scanf("%d%d%lf",&p1,&q1,&o);
        add(p1,q1,o);
      }
    for (;r-l>1e-5;)
      {
        mid=(l+r)/2;
        if (spfa(mid)) l=mid;else r=mid; 
      } 
    printf("%.2f",l);
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值