poj3621 Sightseeing Cows 【最大比例环 01分数规划】

链接:http://poj.org/problem?id=3621

题意:

n个点,m条边,点上有收益b,边上有花费c,点和边都可以多次经过,花费要重复算,但是收益不能重复算。

现在要你找到一个环,使得sigma(b)/sigma(c)最大。

分析:

由于可能出现复杂的环,比如环套环,其中一个环的收益和花费为b1、c1,另一个为b2,c2,重复的花费为s。

假设b1/c1>b2/c2,如果(b1+b2)/(c1+c2+s)>b1/c1。

可以得出 b2/(c2+s)>b1/c1 与假设矛盾,所以单个环的比例比多个环和在一起的比例大,所以不用考虑环套环。

现在我们只用讨论简单环了,我们将点的花费放到边上,这样收益和花费都在一起了,我们就可以用01分数规划的思路来做了。

如果存在一个比例L使得sigma(b)-L*sigma(c)是个正权环(取反跑DFS版的判负环),那么这个L可行,我们二分找到这个最大的L。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 1010
#define Mm 2000005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CLRS(a,b,Size) memset((a),(b),sizeof((a[0]))*(Size+1))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
const double eps=1e-6;
struct edge {
    int v,next;
    double w;
}e[Mm];
int tot,head[Mn];
void addedge(int u,int v,double w) {
    e[tot].v=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot++;
}
double dis[Mn],a[Mn];
int vis[Mn],st,n;
bool dfs(int u,double x) {
    vis[u]=st;
    for(int i=head[u];~i;i=e[i].next) {
        int v=e[i].v;
        if(dis[v]>dis[u]+e[i].w*x-a[u]) {
            dis[v]=dis[u]+e[i].w*x-a[u];
            if(vis[v]==st) return true;
            if(dfs(v,x)) return true;
        }
    }
    vis[u]=0;
    return false;
}
bool check(double x) {
    CLR(vis,0);
    CLR(dis,0);
    for(st=1;st<=n;st++) {
        if(dfs(st,x)) return true;
    }
    return false;
}
int main() {
    int m,u,v;
    double w;
    while(~scanf("%d%d",&n,&m)) {
        CLR(head,-1);tot=0;
        for(int i=1;i<=n;i++){
            scanf("%lf",&a[i]);
        }
        for(int i=0;i<m;i++) {
            scanf("%d%d%lf",&u,&v,&w);
            addedge(u,v,w);
        }
        double l=0.0,r=10000.0;
        while((r-l)>eps) {
            double mid=(l+r)/2;
            if(check(mid)) l=mid;
            else r=mid;
        }
        printf("%.2f\n",l);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值