洛谷P2868 奶牛观光 01分数规划学习

题目链接[USACO07DEC]Sightseeing Cows G - 洛谷

01分数规划问题简单说来就是给你一堆物品,每件物品有两个属性a,b。每个物品都有选或不选两种情况,设定一个参数x[i],当x[i] = 0时代表物品i不选,x[i] = 1时代表物品选。我们需要从中取出若干物品,也就是求出

的最值,我们移一下项,式子变成了

求出满足式子的R的最(大/小)值

提公因式得到

我们可以发现,式子左边的值是随着R的增大而减小的。

我们以求最大值为例,对于一个确定的R值,当左边的式子的值大于零时,说明有更大的R使得式子更接近0,就可以用二分来做。//别人博客里的,拿来用一下

这题中的 R 就是奶牛平均乐趣值要求的最大值,a[i]是每个点的乐趣值,b[i]是各个道路所耗费的时间,再利用SPFA求负环的技巧求出图中的正环(因为是求最大值,所以找正环),二分以下即可求解

还有注意精度 l与r的差值应该在0.0001之间

AC代码如下
 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int n,m,idx;
int e[5010],ne[5010],wi[5010],h[5010],wj[1010];
double dist[1010];
bool vis[1010];
int cnt[1010];
void add(int a,int b,int c)
{
  idx++;
  e[idx]=b;
  wi[idx]=c;
  ne[idx]=h[a];
  h[a]=idx;
}
bool check(double mid)
{
  memset(dist,0,sizeof(dist));
  memset(vis,0,sizeof(vis));
  memset(cnt,0,sizeof(cnt));
  queue<int> q;
  for(int i=1;i<=n;i++) q.push(i),vis[i]=false;
  while(q.size())
  {
  int k=q.front();
  q.pop();
  vis[k]=false;
  for(int i=h[k];i;i=ne[i])
  {
    int j=e[i];
    if(dist[j]<dist[k]+wj[k]-mid*wi[i])
    {
         dist[j]=dist[k]+wj[k]-mid*wi[i];
         cnt[j]=cnt[k]+1;
         if(cnt[j]>=n) return true;
         if(!vis[j])
         {
              q.push(j);
              vis[j]=true;
         }
    }
  }
  
  }

  return false;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&wj[i]);
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
double r=1000,l=0;
while(r-l>1e-4)
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}

printf("%.2lf",l);
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值