首页 > 其他 > 详细

「USACO2001OPEN」地震

时间:2020-02-04 21:01:49      阅读:81      评论:0      收藏:0      [点我收藏+]

有一只施工队要修路,每条路有成本 \(c\) 和需要的时间 \(t\) ,总收入为 \(f\) ,要求选择数条边使图联通,求 \(f-\frac{\sum{c_i*x_i}}{\sum{t_i*x_i}}=ans\) 的最大值 ( \(x_i\)\(0\)\(1\) 表示选或不选)

Luogu

分析

我们设答案为 \(k\) ,则有
\[\frac{f-\sum{c_i}}{\sum{t_i}}=k\]
移项得
\[f-(\sum{c_i}+k*\sum{t_i})=0\]
也就是
\[f-\sum{(c_i+k*t_i)}=0\]
我们设
\[f(k)=f-\sum(c_i+k*t_i)\]
\(f(k)>0\) 时,则有比 \(k\) 更好的答案;当 \(f(k)=0\) 时, \(k\) 一定是最优解;而当 \(f(k)<0\) 时, 答案要小于 \(k\)

所以我们可以二分枚举 \(k\) 值,设 \(v_i=c_i+k*t_i\) ,每条边按 \(v\) 从小到大排序,每次用 Kruskal 求出最小生成树,计算 \(\sum{v_i}\) ,判断其是否小于等于 \(f\) 即可。

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 401
#define M 10003
#define eps 1e-7
#define il inline
#define re register
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;

template <typename T> inline void read(T &x) {
    T f = 1; x = 0; char c;
    for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    x *= f;
}

struct edge {
    int u, v, c, t;
    double w;
    friend bool operator < (edge x, edge y) { return x.w < y.w; }
} e[M];

int n, m, f, cnt;
int fa[N];

int find(int x) { return fa[x] == x ? x : find(fa[x]); }

double Kruskal() {
    double res = 0; cnt = 0;
    sort(e + 1, e + 1 + m);
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i <= m; ++i) {
        int f1 = find(e[i].u), f2 = find(e[i].v);
        if (f1 == f2) continue;
        fa[f2] = f1; res += e[i].w;
        if (++cnt == n - 1) break;
    }
    return res;
}

bool check(double t) {
    for (int i = 1; i <= m; ++i) e[i].w = e[i].c + e[i].t * t;
    return Kruskal() <= (double)f;
}

int main() {
    int u, v, c, t;
    read(n), read(m), read(f);
    for (int i = 1; i <= m; ++i)
        read(e[i].u), read(e[i].v), read(e[i].c), read(e[i].t);
    double l = 0, r = 1000000000.0;
    while (r - l > eps) {
        double mid = (l + r) / 2.0;
        if (check(mid)) l = mid;
        else r = mid;
    }
    printf("%.4lf", l);
    return 0;
}

「USACO2001OPEN」地震

原文:https://www.cnblogs.com/hlw1/p/12260548.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!