题目
题目传送门
输入输出样例
输入样例
5 6
1 2 1 1
4 1 6 2
5 4 8 1
2 3 2 2
5 2 4 1
3 5 6 4
输出样例
2.3
题解
- 本题我们要求 max ∑ v i ∑ p i \max{\frac{\sum{v_i}}{\sum{p_i}}} max∑pi∑vi
- 这是01分数规划的基本形式。
- 设 a n s ans ans为最优解,则有 ∑ v i ∑ p i ≤ a n s \frac{\sum{v_i}}{\sum{p_i}}\leq ans ∑pi∑vi≤ans
- 化简得 0 ≤ a n s ∗ ∑ p i − ∑ v i 0 \leq ans*\sum{p_i}-\sum{v_i} 0≤ans∗∑pi−∑vi
- 所以只要以 a n s ∗ ∑ p i − ∑ v i ans*\sum{p_i}-\sum{v_i} ans∗∑pi−∑vi为边权的图不存在负环即可更新答案。
- 在建图时可找一个超级源点(如:0)向其余点连权值为0的边可减少时间复杂度。
code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 7e3 + 100;
const int maxm = 2e4 + 100;
template <typename T>
inline void read(T &s) {
s = 0;
T w = 1, ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
s *= w;
}
int n, m, tot;
int lin[maxn];
bool flag;
bool vis[maxn];
double dis[maxn];
struct node {
int next, to, v, p;
double dis;
} edge[maxm];
inline void add(int from, int to, int v, int p) {
edge[++tot].to = to;
edge[tot].v = v;
edge[tot].p = p;
edge[tot].dis = 0.00;
edge[tot].next = lin[from];
lin[from] = tot;
}
void spfa(int u) {
vis[u] = true;
for (int i = lin[u]; i; i = edge[i].next) {
if (flag) return ;
int v = edge[i].to;
if (dis[v] > dis[u] + edge[i].dis) {
dis[v] = dis[u] + edge[i].dis;
if (vis[v]) {
if (dis[v] < 0) flag = true;
}
else spfa(v);
}
}
vis[u] = false;
}
int main() {
// freopen("1.in", "r", stdin);
read(n), read(m);
for (int i = 1; i <= m; ++i) {
int x, y, v, p;
read(x), read(y), read(v), read(p);
add(x, y, v, p);
}
double l = 0.00, r = 233.33;
while (l + 0.01 < r) {
double mid = (l + r) / 2;
memset(dis, 0, sizeof(dis));
memset(vis, false, sizeof(vis));
for (int i = 1; i <= tot; ++i)
edge[i].dis = (double)edge[i].p * mid - edge[i].v;
flag = false;
for (int i = 1; i <= n; ++i) {
spfa(i);
if (flag) break;
}
if (flag) l = mid;
else r = mid;
}
if (l) printf("%.1lf\n", r);
else puts("-1");
return 0;
}