类型:分数规划
题目:牛可以从任意点出发, 每个点有欢乐值, 一个点可以去多次, 但是欢乐值只增加一次, 每条边有时间消耗, 求
一条回路使得 总欢乐值/总时间 最大
思路:设当前答案为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;
}