poj 3621 Sightseeing Cows

32 篇文章 0 订阅
类型:分数规划

题目:牛可以从任意点出发, 每个点有欢乐值, 一个点可以去多次, 但是欢乐值只增加一次, 每条边有时间消耗, 求

一条回路使得 总欢乐值/总时间 最大

来源:USACO 2007 December Gold

思路:设当前答案为ans,构造新边edge(u, v) * ans - f[v]。假设存在负环,则有

edge(u1, v1) * ans - f[v1] + edge(v1, v2) * ans - f[v2] ... < 0

变形得: (edge(u, v)) / (f[v]) > ans

那么可知ans小了

二分ans,每次判断负环回路

参考:http://helanic.yo2.cn/articles/pkupoj-3621-sightseeing-cows.html

// poj 3621 - Sightseeing Cows
// 272K	625MS
#include <iostream>
#include <queue>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;

#define MAXN 1010
#define EPS 0.0001
#define INF 0x7f7f7f7f

bool vis[MAXN];
int L, P;
int cnt[MAXN], head[MAXN];
double f[MAXN];
double l, r, mid;
double dist[MAXN];
struct edge{
    int v, nxt;
    double w;
}e[MAXN * 5];

int spfa() {
    int i;
    double newedge;

    memset(vis, false, sizeof(vis));
    memset(cnt, 0, sizeof(cnt));
    for(i = 1; i <= L; ++i) dist[i] = INF;
    dist[1] = 0;
    queue<int> q;
    q.push(1);
    vis[1] = true;
    ++cnt[1];
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(i = head[u]; i != -1; i = e[i].nxt) {
            int v = e[i].v;
            // !!!
            newedge = e[i].w * mid - f[v];
            if(newedge + dist[u] < dist[v]) {
                dist[v] = newedge + dist[u];
                if(!vis[v]) {
                    q.push(v);
                    vis[v] = true;
                    if((++cnt[v]) >= L)
                        return -1;
                }
            }
        }
    }
    return 1;
}

void init() {
    int i, u, v;
    double w;

    memset(head, -1, sizeof(head));
    for(i = 1; i <= L; ++i)
        scanf("%lf", &f[i]);
    for(i = 0; i != P; ++i) {
        scanf("%d %d %lf", &u, &v, &w);
        e[i].v = v, e[i].w = w;
        e[i].nxt = head[u];
        head[u] = i;
    }
}

double proc() {
    l = 0.0, r = 1000.0;
    while(r - l > EPS) {
        mid = (l + r) / 2.0;
        (spfa() < 0) ? l = mid : r = mid;
    }
    return mid;
}

int main() {
    while(scanf("%d %d", &L, &P) == 2) {
        init();
        double x = proc();
        printf("%.2lf\n", x);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值