# POJ3621_Sightseeing Cows
题意:
给定一张有向图,每个点有权值 \(fun[i]\) ,每条边有权值 \(time[i]\)
要求找出一个环,使得环上所有点的点权和除以所有边的边权和最大
解:
首先,显然,这是一道01分数规划题
参照分数规划的套路
假定当前二分的值为 \(mid\) ,有环 \(S=(\{v\},\ \{e\})\) ,环有 \(t\) 个点和 \(t\) 条边
考虑是否存在 \(S\) 使得
\[\sum_{i=1}^{t}(mid*time_{i}-fun_{i})<0 \\
即\quad\exists S=(\{v\},\ \{e\})\quad使得\quad mid<\frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}}
\]
此时答案应该大于 \(mid\)
若不存在这样的 \(S\) ,则
\[\forall S,\quad mid\geqslant \frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}}
\]
此时答案应该小于等于 \(mid\)
问题又来了,如何判断是否存在 \(S\) ?
有这样的办法:
在每次选定 \(mid\) 之后
新建一个与原图一样的图,但是没有点权,只有边权,并对边权值做如下修改
\(\forall e_{i}=edge(x\ ->\ y),\quad weight(x\ ->\ y)=mid*time_{e_{i}}-fun_{x}\)
这样建图有什么好处?
这样,若要判定 \(\sum_{i=1}^{t}(mid*time_{i}-fun_{i})<0\) ,刚好对应图中存在“负环”
由此,在新建的图上跑SPFA,有负环说明 \(mid<\frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}}\)
没有则说明 \(mid\geqslant \frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}}\)
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int M = 5010;
int n, m;
double l, r, mid, eps;
int head[N], nxt[M], ver[M], tot;
int thead[N], tnxt[M], tver[M], ttot;
double tedge[M], edge[M], fun[N];
void add(int u, int v, double w) {
ver[++tot] = v; edge[tot] = w;
nxt[tot] = head[u]; head[u] = tot;
}
void tadd(int u, int v, double w) {
tver[++ttot] = v; tedge[ttot] = mid * w - fun[u];
tnxt[ttot] = thead[u]; thead[u] = ttot;
}
void build() {
memset(thead, 0, sizeof thead);
memset(tnxt, 0, sizeof tnxt);
memset(tver, 0, sizeof tver);
memset(tedge, 0, sizeof tedge);
ttot = 0;
for (int i = 1; i <= n; i++) {
int x = i;
for (int j = head[x]; j; j = nxt[j]) {
int y = ver[j];
tadd(x, y, edge[j]);
}
}
}
queue<int> q;
double dis[N]; int cnt[N]; bool vis[N];
bool check() {
while (q.size()) q.pop();
for (int i = 1; i <= n; i++) {
vis[i] = true;
q.push(i);
dis[i] = cnt[i] = 0;
}
while (!q.empty()) {
int x = q.front(); q.pop();
vis[x] = false;
for (int i = thead[x]; i; i = tnxt[i]) {
int y = tver[i];
double z = tedge[i];
if (dis[y] > dis[x] + z) {
dis[y] = dis[x] + z;
if (!vis[y]) {
q.push(y); vis[y] = true;
}
cnt[y] = cnt[x] + 1;
if (cnt[y] >= n) return true;
}
}
}
return false;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lf", &fun[i]);
for (int i = 1; i <= m; i++) {
int u, v; double w;
scanf("%d%d%lf", &u, &v, &w);
add(u, v, w);
}
l = 0; r = 1000; eps = 0.000000000001;
while (l + eps < r) {
mid = (l + r) / 2;
build();
if (check()) l = mid;
else r = mid;
}
printf("%.2lf", l);
return 0;
}

浙公网安备 33010602011771号