P1642 规划 01分数规划+树形DP

$ \color{#0066ff}{ 题目描述 }$

某地方有N个工厂,有N-1条路连接它们,且它们两两都可达。每个工厂都有一个产量值和一个污染值。现在工厂要进行规划,拆除其中的M个工厂,使得剩下的工厂依然连成一片且 总产量/总污染 的值最大。

\(\color{#0066ff}{输入格式}\)

第一行N M(1<N<100,1<=M<N),表示工厂个数和要拆除的个数。

第二行N个正整数,表示每个工厂的产值[1..10000]

第三行N个正整数,表示每个工厂的污染值[1..10000]

接着N-1行,每行两个正整数a b(1<=a,b<=N)表示a,b之间相连。

\(\color{#0066ff}{输出格式}\)

总产量/总污染 的最大值,保留一位小数。

\(\color{#0066ff}{输入样例}\)

3 2
2 3 4
1 1 1
1 2
2 3

\(\color{#0066ff}{输出样例}\)

4.0

\(\color{#0066ff}{数据范围与提示}\)

none

\(\color{#0066ff}{题解}\)

显然是01分数规划问题,那么二分答案

现在的问题是找一个大小为m的联通块

经典树形DP,\(f[i][j]\)为以i为根子树选j个点的最大值(i必选)

跑树形背包即可,注意i必选的限制,所以要对所有点取max

#include<bits/stdc++.h>
#define LL long long
LL read() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
template<class T> bool chkmax(T &a, const T &b) { return a < b? a = b, 1 : 0; }
template<class T> bool chkmin(T &a, const T &b) { return b < a? a = b, 1 : 0; }
const int inf = 0x7fffffff;
const int maxn = 555;
const double eps = 1e-6;
int n, m, a[maxn], b[maxn], siz[maxn];
double f[maxn][maxn], val[maxn], mx;
std::vector<int> G[maxn];
void dfs(int x, int fa) {
    siz[x] = 1;
    f[x][0] = 0, f[x][1] = val[x];
    for(auto to : G[x]) {
        if(to == fa) continue;
        dfs(to, x);
        siz[x] += siz[to];
        for(int i = std::min(m, siz[x]); i >= 1; i--)
            for(int j = 0; j < i; j++)
                chkmax(f[x][i], f[x][i - j] + f[to][j]);
    }
    chkmax(mx, f[x][m]);
}
bool ok(double mid) {
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= m; j++) 
            f[i][j] = -inf;
    for(int i = 1; i <= n; i++) val[i] = (double)(a[i] - mid * b[i]);
    mx = -1e18;
    dfs(1, 0);
    return mx >= 0;
}

int main() {
    n = read(), m = n - read();
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int i = 1; i <= n; i++) b[i] = read();
    int x, y;
    for(int i = 1; i < n; i++) {
        x = read(), y = read();
        G[x].push_back(y);
        G[y].push_back(x);
    }
    double l = 0, r = 105050;
    while(r - l > eps) {
        double mid = (l + r) / 2.0;
        if(ok(mid)) l = mid;
        else r = mid;
    }
    printf("%.1f\n", l);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/olinr/p/10652961.html